File Coverage

blib/lib/Parse/PlainConfig.pm
Criterion Covered Total %
statement 382 400 95.5
branch 105 144 72.9
condition 35 48 72.9
subroutine 36 36 100.0
pod 9 9 100.0
total 567 637 89.0


line stmt bran cond sub pod time code
1             # Parse::PlainConfig -- Parsing Engine for Parse::PlainConfig
2             #
3             # (c) 2002 - 2023, Arthur Corliss ,
4             #
5             # $Id: lib/Parse/PlainConfig.pm, 3.07 2024/01/10 13:32:06 acorliss Exp $
6             #
7             # This software is licensed under the same terms as Perl, itself.
8             # Please see http://dev.perl.org/licenses/ for more information.
9             #
10             #####################################################################
11              
12             #####################################################################
13             #
14             # Environment definitions
15             #
16             #####################################################################
17              
18             package Parse::PlainConfig;
19              
20 11     11   1569416 use 5.008;
  11         37  
21              
22 11     11   57 use strict;
  11         20  
  11         269  
23 11     11   75 use warnings;
  11         18  
  11         656  
24 11     11   95 use vars qw($VERSION);
  11         21  
  11         1172  
25              
26             ($VERSION) = ( q$Revision: 3.07 $ =~ /(\d+(?:\.(\d+))+)/sm );
27              
28 11     11   7428 use Class::EHierarchy qw(:all);
  11         97505  
  11         2097  
29 11     11   5165 use Parse::PlainConfig::Constants qw(:all);
  11         28  
  11         1830  
30 11     11   5006 use Parse::PlainConfig::Settings;
  11         31  
  11         3843  
31 11     11   7698 use Text::ParseWords;
  11         16956  
  11         853  
32 11     11   4864 use Text::Tabs;
  11         8598  
  11         1229  
33 11     11   78 use Fcntl qw(:seek :DEFAULT);
  11         19  
  11         4104  
34 11     11   75 use Paranoid;
  11         18  
  11         534  
35 11     11   58 use Paranoid::Debug;
  11         16  
  11         820  
36 11     11   6727 use Paranoid::IO;
  11         180579  
  11         1256  
37 11     11   8117 use Paranoid::IO::Line;
  11         42251  
  11         1024  
38 11     11   86 use Paranoid::Input qw(:all);
  11         21  
  11         1303  
39 11     11   5917 use Paranoid::Glob;
  11         59874  
  11         574  
40              
41 11     11   110 use base qw(Class::EHierarchy);
  11         21  
  11         1128  
42              
43 11     11   66 use vars qw(@_properties @_methods %_parameters %_prototypes);
  11         23  
  11         1211  
44              
45             #####################################################################
46             #
47             # Module code follows
48             #
49             #####################################################################
50              
51             sub _findAllClasses {
52              
53             # Purpose: Returns a list of all parent class names
54             # Returns: Array of scalars
55             # Usage: @pclasses = _findAllClasses(ref $obj);
56              
57 42     42   103 my $class = shift;
58 42         85 my ( @classes, %c, $c, @rv );
59              
60 42         153 subPreamble( PPCDLEVEL3, '$', $class );
61              
62             # Pull all parent class and recursively loop
63             {
64 11     11   72 no strict 'refs';
  11         24  
  11         3498  
  42         5082  
65              
66 42 50       75 if ( defined *{"${class}::ISA"}{ARRAY} ) {
  42         317  
67              
68 42         70 foreach $c ( @{ *{"${class}::ISA"}{ARRAY} } ) {
  42         68  
  42         157  
69 32         263 push @classes, _findAllClasses($c);
70             }
71              
72             push @classes, $class
73             if scalar @classes
74 30         141 or grep { $_ eq __PACKAGE__ }
75 42 100 100     164 @{ *{"${class}::ISA"}{ARRAY} };
  40         66  
  40         186  
76             }
77             }
78              
79             # Consolidate redundant entries
80 42         109 foreach $c (@classes) {
81 14 50       87 push @rv, $c unless exists $c{$c};
82 14         66 $c{$c} = 1;
83             }
84              
85 42         140 subPostamble( PPCDLEVEL3, '@', @rv );
86              
87 42         3687 return @rv;
88             }
89              
90             sub _initialize {
91              
92             # Purpose: Initialize config object and loads class defaults
93             # Returns: Boolean
94             # Usage: $rv = $obj->_initialize(@args);
95              
96 10     10   1928747 my $obj = shift;
97 10         33 my $class = ref $obj;
98 10         29 my $rv = 1;
99 10         39 my ( @classes, $settings, %new, %_globals, %_parameters, %_prototypes );
100              
101 10         83 subPreamble( PPCDLEVEL1, '$$', $obj, $class );
102              
103             # Create & adopt the settings object
104 10         2188 $settings = new Parse::PlainConfig::Settings;
105 10         9451 $obj->adopt($settings);
106 10         2191 $settings->alias('settings');
107              
108             # Get a list of all parent classes
109 10         848 @classes = ( _findAllClasses($class) );
110              
111             # Read in class global settings
112 10 50       49 unless ( __PACKAGE__ eq $class ) {
113              
114 10         29 foreach $class (@classes) {
115 12 50       204 if ( defined *{"${class}::_globals"} ) {
  12         96  
116 12         75 pdebug( 'loading globals from %s', PPCDLEVEL2, $class );
117              
118             {
119 11     11   112 no strict 'refs';
  11         23  
  11         2024  
  12         521  
120              
121 12         26 %new = %{ *{"${class}::_globals"}{HASH} };
  12         22  
  12         140  
122             }
123              
124 12 100       66 if ( scalar keys %new ) {
125 10         66 foreach ( keys %new ) {
126 50         3302 $_globals{$_} = $new{$_};
127             pdebug( 'overriding %s with (%s)',
128 50         160 PPCDLEVEL3, $_, $_globals{$_} );
129 50 50       2536 $rv = 0 unless $settings->set( $_, $_globals{$_} );
130             }
131             }
132             }
133             }
134              
135 10         574 foreach $class (@classes) {
136 12 50       517 if ( defined *{"${class}::_parameters"} ) {
  12         133  
137 12         69 pdebug( 'loading parameters from %s', PPCDLEVEL2, $class );
138              
139             {
140 11     11   210 no strict 'refs';
  11         92  
  11         2917  
  12         568  
141              
142 12         76 %new = %{ *{"${class}::_parameters"}{HASH} };
  12         22  
  12         154  
143             }
144              
145 12 50       64 if ( scalar keys %new ) {
146 12         52 %_parameters = ( %_parameters, %new );
147 12         87 $settings->set( 'property types', %_parameters );
148 12         1003 foreach ( keys %new ) {
149              
150 52         7708 pdebug( 'creating property %s', PPCDLEVEL3, $_ );
151 52 100       2624 unless (
    50          
152             _declProperty(
153             $obj, $_,
154             CEH_PUB | (
155             $_parameters{$_} == PPC_HDOC
156             ? PPC_SCALAR
157             : $_parameters{$_}
158             ),
159             )
160             ) {
161 0         0 $rv = 0;
162 0         0 last;
163             }
164              
165             # merge property regex
166             $settings->merge(
167 52         2574 'property regexes',
168             $_,
169 52         217 qr#(\s*)(\Q$_\E)\s*\Q@{[ $settings->delimiter ]}\E\s*(.*)#s
170             );
171             }
172             }
173             }
174              
175 12 50       2283 if ( defined *{"${class}::_prototypes"} ) {
  12         88  
176 12         54 pdebug( 'loading prototypes from %s', PPCDLEVEL2, $class );
177              
178             {
179 11     11   107 no strict 'refs';
  11         29  
  11         42710  
  12         565  
180              
181 12         27 %new = %{ *{"${class}::_prototypes"}{HASH} };
  12         25  
  12         91  
182             }
183              
184 12 50       48 if ( scalar keys %new ) {
185 12         57 %_prototypes = ( %_prototypes, %new );
186 12         62 $settings->set( 'prototypes', %_prototypes );
187 12         882 foreach ( keys %new ) {
188              
189             # merge property meta-data
190 22         2097 $settings->merge(
191             'prototype regexes',
192             $_,
193 22         117 qr#(\s*)(\Q$_\E)\s+(\S+)\s*\Q@{[ $settings->delimiter ]}\E\s*(.*)#s
194             );
195             }
196             }
197             }
198             }
199             }
200              
201             # Store all parent classes
202 10         1949 $settings->set( '_ppcClasses', @classes );
203              
204             # Load the defaults
205 10         762 $rv = $obj->parse( $obj->default );
206              
207 10         107 subPostamble( PPCDLEVEL1, '$', $rv );
208              
209 10         1089 return $rv;
210             }
211              
212             sub settings {
213              
214             # Purpose: Returns object reference to the settings object
215             # Returns: Object reference
216             # Usage: $settings = $obj->settings;
217              
218 276     276 1 471 my $obj = shift;
219              
220 276         956 return $obj->getByAlias('settings');
221             }
222              
223             sub _default {
224              
225             # Purpose: Returns the DATA block from the calling
226             # Returns: Array
227             # Usage: @lines = $obj->_default;
228              
229 23     23   57 my $obj = shift;
230 23         65 my $class = shift;
231 23         56 my ( $fn, @chunk, @lines );
232              
233 23         103 subPreamble( PPCDLEVEL2, '$', $obj );
234              
235 23         2437 $class =~ s#::#/#sg;
236 23         62 $class .= '.pm';
237 23         90 $fn = $INC{$class};
238              
239 23         77 pdebug( 'attempting to read from %s', PPCDLEVEL3, $fn );
240 23 50       1058 if ( popen( $fn, O_RDONLY ) ) {
241              
242             # Read in file
243 23   66     27816 while ( sip( $fn, @chunk ) and @chunk ) { push @lines, @chunk }
  23         109645  
244              
245             # empty all lines prior to __DATA__
246 23   100     84504 while ( @lines and $lines[0] !~ /^\s*__DATA__\s*$/s ) {
247 711         2474 shift @lines;
248             }
249 23         56 shift @lines;
250              
251             # empty all lines after __END__
252 23 100 100     370 if ( @lines and grep /^\s*__END__\s*$/s, @lines ) {
253 12   66     84 while ( @lines and $lines[-1] !~ /^\s*__END__\s*$/s ) {
254 48         213 pop @lines;
255             }
256 12         25 pop @lines;
257             }
258 23         110 pseek( $fn, 0, SEEK_SET );
259             }
260              
261 23         13023 subPostamble( PPCDLEVEL2, '@', @lines );
262              
263 23 50       3758 return wantarray ? @lines : join '', @lines;
264             }
265              
266             sub default {
267              
268             # Purpose: Returns the DATA block from the specified class,
269             # or the object class if not specified
270             # Returns: Array
271             # Usage: @lines = $obj->default;
272             # Usage: @lines = $obj->default($class);
273              
274 19     19 1 1605 my $obj = shift;
275 19         126 my @classes = $obj->getByAlias('settings')->get('_ppcClasses');
276 19         1989 my ( $class, @rv );
277              
278 19         107 subPreamble( PPCDLEVEL1, '$', $obj );
279              
280 19         2276 foreach $class (@classes) {
281 23         163 push @rv, $obj->_default($class);
282             }
283              
284 19         91 subPostamble( PPCDLEVEL1, '@', @rv );
285              
286 19         3301 return @rv;
287             }
288              
289             sub get {
290              
291             # Purpose: Returns the value of the specified parameter
292             # Returns: Scalar/List/Hash
293             # Usage: $val = $obj->get('foo');
294              
295 78     78 1 36243 my $obj = shift;
296 78         165 my $p = shift;
297 78         179 my ( $valp, $valt, $rv );
298              
299 78         341 subPreamble( PPCDLEVEL1, '$$', $obj, $p );
300              
301 78 50       10885 if ( defined $p ) {
302 78         356 $valp = scalar grep /^\Q$p\E$/s, $obj->properties;
303 78 100       5807 if ($valp) {
304 73         254 ($valt) = $obj->settings->subset( 'property types', $p );
305 73 100       7848 $valt = PPC_SCALAR if $valt == PPC_HDOC;
306             }
307             }
308             $obj->error(
309 78 100       251 pdebug( 'specified invalid parameter name: %s', PPCDLEVEL1, $p ) )
310             unless $valp;
311              
312             # Return values get a little dicier because of different data types
313 78 100       194 if ( defined $valt ) {
314 73 100       234 if ( $valt == PPC_SCALAR ) {
    100          
315 35         179 $rv = $obj->SUPER::get($p);
316 35         2134 subPostamble( PPCDLEVEL1, '$', $rv );
317 35         4358 return $rv;
318             } elsif ( $valt == PPC_ARRAY ) {
319 31         116 $rv = [ $obj->SUPER::get($p) ];
320 31         1786 subPostamble( PPCDLEVEL1, '@', @$rv );
321 31         4027 return @$rv;
322             } else {
323 7         46 $rv = { $obj->SUPER::get($p) };
324 7         531 subPostamble( PPCDLEVEL1, '%', %$rv );
325 7         1298 return %$rv;
326             }
327             }
328              
329 5         22 subPostamble( PPCDLEVEL1, '$', $rv );
330              
331 5         447 return $rv;
332             }
333              
334             sub set {
335              
336             # Purpose: Assigns the desired values to the specified parameter
337             # Returns: Boolean
338             # Usage: $rv = $obj->set($prop, @values);
339              
340 17     17 1 3269 my $obj = shift;
341 17         42 my $p = shift;
342 17         68 my @vals = @_;
343 17         82 my %propTypes = $obj->settings->propertyTypes;
344 17         1188 my ( $valp, $rv );
345              
346 17         105 subPreamble( PPCDLEVEL1, '$$@', $obj, $p, @vals );
347              
348 17 50       2844 if ( defined $p ) {
349 17         68 $valp = scalar grep /^\Q$p\E$/s, $obj->properties;
350             }
351             $obj->error(
352 17 100       1274 pdebug( 'specified invalid parameter name: %s', PPCDLEVEL1, $p ) )
353             unless $valp;
354              
355 17 100       55 if ($valp) {
356 13 50       41 if (@vals) {
357              
358             # Set whatever's assigned
359 13         62 $rv = $obj->SUPER::set( $p, @vals );
360             } else {
361              
362             # Assume that no values means empty/undef
363 0 0 0     0 if ( $propTypes{$p} == PPC_SCALAR
364             or $propTypes{$p} == PPC_HDOC ) {
365 0         0 $rv = $obj->SUPER::set( $p, undef );
366             } else {
367 0         0 $rv = $obj->empty($p);
368             }
369             }
370             }
371              
372 17 100       1167 subPostamble( PPCDLEVEL1, '$', $valp ? $rv : undef );
373              
374 17 100       2216 return $valp ? $rv : undef;
375             }
376              
377             sub _snarfBlock (\@\$\$$) {
378              
379             # Purpose: Finds and returns the block with the value
380             # string extracted.
381             # Returns: Boolean
382             # Usage: $rv = _snarfBlock(@lines, $val);
383              
384 138     138   238 my $lref = shift;
385 138         220 my $pref = shift;
386 138         213 my $vref = shift;
387 138         230 my $settings = shift;
388 138         625 my $obj = $settings->parent;
389 138         2812 my %regex = $settings->propertyRegexes;
390 138         8779 my %pregex = $settings->prototypeRegexes;
391 138         7728 my %propTypes = $settings->propertyTypes;
392 138         8087 my %prototypes = $settings->prototypes;
393 138         7402 my $subi = $settings->subindentation;
394 138         7175 my ( $rv, $indent, $prop, $proto, $trailer, $iwidth, $line, $preg );
395              
396 138         538 subPreamble( PPCDLEVEL2, '$$$$', $lref, $pref, $vref, $settings );
397              
398             # Match line to a property/prototype declaration
399             #
400             # First try to match against properties
401 138         20338 foreach ( keys %regex ) {
402 550 100       33050 if ( $$lref[0] =~ /^$regex{$_}$/s ) {
403 79         510 ( $indent, $prop, $trailer ) = ( $1, $2, $3 );
404 79         133 $rv = 1;
405 79         157 shift @$lref;
406 79         225 last;
407             }
408             }
409 138 50 66     815 unless ( $rv and defined $prop and length $prop ) {
      66        
410 59         195 foreach ( keys %pregex ) {
411 103 100       7452 if ( $$lref[0] =~ /^$pregex{$_}$/s ) {
412 54         364 ( $indent, $proto, $prop, $trailer ) = ( $1, $2, $3, $4 );
413 54         101 $rv = 1;
414 54         111 shift @$lref;
415 54         195 last;
416             }
417             }
418             }
419              
420             # Define all prototyped properties
421 138 100 66     580 if ( defined $proto and length $proto ) {
422 54 50 33     356 if ( defined $prop and length $prop ) {
423              
424 54 100       255 if ( exists $regex{$prop} ) {
425 3         17 $obj->error(
426             pdebug(
427             'token (%s) for prototype (%s) attempted to override property',
428             PPCDLEVEL1,
429             $prop,
430             $proto
431             ) );
432 3         10 $rv = 0;
433             } else {
434              
435 51 100       149 if ( exists $propTypes{$prop} ) {
436              
437             # Make sure they haven't been previously defined,
438             # or if they have, they match the same type
439 27 100       122 unless ( $propTypes{$prop} == $prototypes{$proto} ) {
440 3         8 $rv = 0;
441 3         14 $obj->error(
442             pdebug(
443             'prototype mismatch with previous declaration: %s',
444             PPCDLEVEL1,
445             $proto
446             ) );
447             pdebug( 'current type: %s prototype: %s',
448             PPCDLEVEL1, $propTypes{$prop},
449 3         16 $prototypes{$proto} );
450             }
451             } else {
452              
453             # Create a new property
454 24         134 pdebug( 'creating property based on prototype %s: %s',
455             PPCDLEVEL3, $proto, $prop );
456              
457             $rv = _declProperty(
458             $obj, $prop,
459             CEH_PUB | (
460             $prototypes{$proto} == PPC_HDOC
461             ? PPC_SCALAR
462 24 50       1562 : $prototypes{$proto}
463             ),
464             );
465              
466             # Record the prop type
467 24 50       1414 if ($rv) {
468             $settings->merge( 'property types',
469 24         157 $prop, $propTypes{$prop} = $prototypes{$proto} );
470 24         1764 ($preg) =
471             $settings->subset( 'prototype registry', $proto );
472 24 100       1460 $preg = [] unless defined $preg;
473 24         60 push @$preg, $prop;
474 24         88 $settings->merge( 'prototype registry',
475             $proto => $preg );
476             } else {
477 0         0 $obj->error(
478             pdebug(
479             'failed to declare prototype: %s %s',
480             PPCDLEVEL1, $proto, $prop
481             ) );
482             }
483             }
484             }
485             } else {
486 0         0 $obj->error(
487             pdebug(
488             'invalid token used for prototype %s: %s', PPCDLEVEL1,
489             $proto, $prop
490             ) );
491 0         0 $rv = 0;
492             }
493             }
494              
495             # Grab additional lines as needed
496 138 100       1866 if ($rv) {
497              
498 127 100       463 if ( $propTypes{$prop} == PPC_HDOC ) {
499              
500             # Snarf all lines until we hit the HDOC marker
501 18         40 $rv = 0;
502 18         84 while (@$lref) {
503 72         189 $line = shift @$lref;
504 72 100       168 if ( $line =~ /^\s*\Q@{[ $settings->hereDoc ]}\E\s*$/s ) {
  72         250  
505 18         951 $rv = 1;
506 18         54 last;
507             } else {
508 54         3976 $line =~ s/^\s{1,$subi}//s;
509 54         275 $trailer .= $line;
510             }
511             }
512              
513             # Error out if we never found the marker
514             $obj->error(
515 18 50       76 pdebug( 'failed to find the here doc marker', PPCDLEVEL1 ) )
516             unless $rv;
517              
518             } else {
519              
520             # All non-HDOCs are handled the same
521 109 50       328 $iwidth = defined $indent ? length $indent : 0;
522 109         309 while (@$lref) {
523              
524             # We're done if this is a line break
525 180 100       867 last if $$lref[0] =~ /^\s*$/s;
526              
527             # We're also done if indentation isn't greater
528             # than the parameter declaration line
529 93         339 ($indent) = ( $$lref[0] =~ /^(\s*)/s );
530 93 100 66     440 last if !defined $indent or $iwidth >= length $indent;
531              
532             # Append content to the trailer
533 72         177 $line = shift @$lref;
534 72         627 $line =~ s/^\s{1,$subi}//s;
535 72         260 pchomp($line);
536 72         1889 $trailer .= $line;
537             }
538             }
539 127 50       1095 $trailer =~ s/\s+$//s if defined $trailer;
540             }
541              
542 138 100       363 if ($rv) {
543 127         593 pchomp($trailer);
544 127         3917 ( $$pref, $$vref ) = ( $prop, $trailer );
545 127         447 pdebug( 'extracted value for %s: %s', PPCDLEVEL3, $prop, $trailer );
546             }
547              
548 138         7551 subPostamble( PPCDLEVEL2, '$', $rv );
549              
550 138         16219 return $rv;
551             }
552              
553             sub _snarfProp {
554              
555             # Purpose: Takes the property value and parses according to its type,
556             # then merges it
557             # Returns: Boolean
558             # Usage: $rv = _snarfProp($obj, $prop, $val);
559              
560 127     127   238 my $obj = shift;
561 127         222 my $prop = shift;
562 127         295 my $val = shift;
563 127         470 my $settings = $obj->settings;
564 127         5910 my %propTypes = $settings->propertyTypes;
565 127         8159 my $ldelim = $settings->listDelimiter;
566 127         6728 my $hdelim = $settings->hashDelimiter;
567 127         6262 my $rv = 1;
568 127         208 my @elements;
569              
570 127         491 subPreamble( PPCDLEVEL2, '$$$', $obj, $prop, $val );
571              
572 127 100 100     19042 if ( $propTypes{$prop} == PPC_HDOC
573             or $propTypes{$prop} == PPC_SCALAR ) {
574              
575             # Here Docs and scalars are merged as-is
576 55         336 $obj->SUPER::set( $prop, $val );
577              
578             } else {
579              
580 72 100       206 if ( $propTypes{$prop} == PPC_ARRAY ) {
581              
582             # Split into a list
583 54         600 @elements = quotewords( qr/\Q$ldelim\E/s, 0, $val );
584 54         9124 foreach (@elements) { s/^\s+//s; s/\s+$//s; }
  123         276  
  123         298  
585              
586             } else {
587              
588             # Split into a hash
589 18         439 @elements =
590             quotewords( qr/(?:\Q$ldelim\E|\Q$hdelim\E)/s, 0, $val );
591 18         8683 foreach (@elements) { s/^\s+//s; s/\s+$//s; }
  144         289  
  144         390  
592              
593             }
594              
595             # merge the list value
596 72         274 pdebug( 'storing in %s: %s', PPCDLEVEL3, $prop, @elements );
597 72         5422 $obj->empty($prop);
598 72         3966 $obj->SUPER::set( $prop, @elements );
599             }
600              
601 127         10110 subPostamble( PPCDLEVEL2, '$', $rv );
602              
603 127         14886 return $rv;
604             }
605              
606             sub parse {
607              
608             # Purpose: Parses passed content and extracts values
609             # Returns: Boolean
610             # Usage: $rv = $obj->parse(@lines);
611              
612 31     31 1 3220 my $obj = shift;
613 31         264 my @lines = @_;
614 31         177 my $settings = $obj->settings;
615 31         1642 my $delim = $settings->delimiter;
616 31         1964 my $cre = qr#^\s*\Q@{[ $settings->comment ]}\E#s;
  31         122  
617 31         2333 my $rv = 1;
618 31         71 my ( $text, $prop, $value, $glob );
619              
620 31         158 subPreamble( PPCDLEVEL1, '$@', $obj, @lines );
621              
622             # Some preprocessing of lines
623 31 100       5175 if (@lines) {
624 29         132 $tabstop = $settings->tabStop;
625 29         1768 @lines = expand(@lines);
626 29         18308 foreach (@lines) {
627 559 100 66     4114 $text =
628             ( defined $text and length $text )
629             ? join "\n", $text, split NEWLINE_REGEX, $_
630             : join "\n", split NEWLINE_REGEX, $_;
631             }
632             }
633              
634 31         115 while (@lines) {
635              
636             # Skip comments and empty lines
637 415 100 100     2941 if ( $lines[0] =~ /^$cre/s
638 260         2822 or $lines[0] =~ /^\s*(?:@{[ NEWLINE_REGEX ]})?$/s ) {
639 276         564 shift @lines;
640 276         892 next;
641             }
642              
643             # Handle "include" statements
644 139 100       569 if ( $lines[0] =~ /^\s*include\s+(.+?)\s*$/s ) {
645 1         11 $glob = new Paranoid::Glob globs => [$1];
646 1         526 shift @lines;
647 1 50       6 $rv = 0 unless $obj->read($glob);
648 1         4 next;
649             }
650              
651             # See if we have property block
652 138 100       561 if ( _snarfBlock( @lines, $prop, $value, $settings ) ) {
653              
654             # Parse the block (but preserve earlier errors)
655 127 50       344 $rv = 0 unless _snarfProp( $obj, $prop, $value );
656              
657             } else {
658              
659 11         74 pdebug( 'discarding invalid input: %s', PPCDLEVEL1, $lines[0] );
660 11         463 shift @lines;
661 11         48 $rv = 0;
662             }
663             }
664              
665 31         128 subPostamble( PPCDLEVEL1, '$', $rv );
666              
667 31         3351 return $rv;
668             }
669              
670             sub read {
671              
672             # Purpose: Reads the passed file(s)
673             # Returns: Boolean
674             # Usage: $rv = $obj->read($filename);
675              
676 5     5 1 2004 my $obj = shift;
677 5         11 my $source = shift;
678 5         11 my ( $rv, @lines );
679              
680 5         22 subPreamble( PPCDLEVEL1, '$$', $obj, $source );
681              
682 5 50       634 if (@_) {
    100          
    50          
683              
684             # Work all entries passed if handed a list
685 0         0 $rv = $obj->read($source);
686 0 0       0 foreach (@_) { $rv = 0 unless $obj->read($_) }
  0         0  
687              
688             } elsif ( ref $source eq '' ) {
689              
690             # Treat all non-reference files as filenames
691 4 50       25 if ( slurp( $source, @lines ) ) {
692 4         25411 $rv = $obj->parse(@lines);
693 4 50       21 pdebug( 'errors parsing %s', PPCDLEVEL1, $source ) unless $rv;
694             } else {
695 0         0 $obj->error(
696             pdebug(
697             'failed to read %s: %s', PPCDLEVEL1,
698             $source, Paranoid::ERROR() ) );
699             }
700              
701             } elsif ( ref $source eq 'Paranoid::Glob' ) {
702              
703             # Handle Paranoid globs specially
704 1         2 $rv = 1;
705 1 50       2 foreach (@$source) { $rv = 0 unless $obj->read($_) }
  1         3  
706              
707             } else {
708              
709             # Handle everything else as if it was a glob
710 0 0       0 if ( slurp( $source, @lines ) ) {
711 0         0 $rv = $obj->parse(@lines);
712 0 0       0 pdebug( 'errors parsing %s', PPCDLEVEL1, $source ) unless $rv;
713             } else {
714 0         0 $obj->error(
715             pdebug(
716             'failed to read %s: %s', PPCDLEVEL1,
717             $source, Paranoid::ERROR() ) );
718             }
719             }
720              
721 5         20 subPostamble( PPCDLEVEL1, '$', $rv );
722              
723 5         562 return $rv;
724             }
725              
726             sub reset {
727              
728             # Purpose: Resets configuration state to defaults
729             # Returns: Boolean
730             # Usage: $rv = $obj->reset;
731              
732 7     7 1 6087 my $obj = shift;
733 7         32 my $settings = $obj->settings;
734 7         363 my %propTypes = $settings->propertyTypes;
735 7         525 my $rv;
736              
737 7         40 subPreamble( PPCDLEVEL1, '$', $obj );
738              
739             # empty all property values
740 7         904 foreach ( keys %propTypes ) {
741 51         2489 pdebug( 'clearing merged values for %s', PPCDLEVEL2, $_ );
742 51 100 100     2057 if ( $propTypes{$_} == PPC_SCALAR or $propTypes{$_} == PPC_HDOC ) {
743 25         66 $obj->SUPER::set( $_, undef );
744             } else {
745 26         81 $obj->empty($_);
746             }
747             }
748 7         400 $rv = $obj->parse( $obj->default );
749              
750 7         92 subPostamble( PPCDLEVEL1, '$', $rv );
751              
752 7         880 return $rv;
753             }
754              
755             sub prototyped {
756              
757             # Purpose: Returns a list of properties that were created with
758             # prototypes
759             # Returns: Array
760             # Usage: @protos = $obj->prototyped;
761             # Usage: @protos = $obj->prototyped($proto);
762              
763 6     6 1 5348 my $obj = shift;
764 6         13 my $proto = shift;
765 6         11 my ( %preg, @prval );
766              
767 6         25 subPreamble( PPCDLEVEL1, '$$', $obj, $proto );
768              
769 6         613 %preg = $obj->settings->get('prototype registry');
770              
771 6 100 66     517 if ( defined $proto and length $proto ) {
772 4 50       10 if ( exists $preg{$proto} ) {
773 4         8 @prval = @{ $preg{$proto} };
  4         11  
774             } else {
775 0         0 pdebug( 'no prototype properties declared w/%s',
776             PPCDLEVEL2, $proto );
777             }
778             } else {
779 2         7 pdebug( 'dumping all declared prototyped properties', PPCDLEVEL2 );
780 2         44 foreach ( keys %preg ) { push @prval, @{ $preg{$_} } }
  5         7  
  5         13  
781             }
782              
783 6         18 subPostamble( PPCDLEVEL1, '@', @prval );
784              
785 6         547 return @prval;
786             }
787              
788             sub error {
789              
790             # Purpose: Sets/gets the last error message
791             # Returns: Scalar/undef
792             # Usage: $errStr = $obj->error;
793             # Usage: $errStr = $obj->error($msg);
794              
795 15     15 1 838 my $obj = shift;
796 15         33 my $msg = shift;
797              
798 15 50       43 if ( defined $msg ) {
799 15         74 $obj->settings->set( 'error', $msg );
800             } else {
801 0         0 $msg = $obj->settings->get('error');
802             }
803              
804 15         1883 return $msg;
805             }
806              
807             1;
808              
809             __END__