File Coverage

blib/lib/Config/General.pm
Criterion Covered Total %
statement 553 690 80.1
branch 300 418 71.7
condition 70 111 63.0
subroutine 37 39 94.8
pod 8 9 88.8
total 968 1267 76.4


line stmt bran cond sub pod time code
1             #
2             # Config::General.pm - Generic Config Module
3             #
4             # Purpose: Provide a convenient way for loading
5             # config values from a given file and
6             # return it as hash structure
7             #
8             # Copyright (c) 2000-2022 Thomas Linden .
9             # All Rights Reserved. Std. disclaimer applies.
10             # Artistic License, same as perl itself. Have fun.
11             #
12             # namespace
13             package Config::General;
14              
15 1     1   66859 use strict;
  1         2  
  1         24  
16 1     1   4 use warnings;
  1         2  
  1         22  
17 1     1   458 use English '-no_match_vars';
  1         2956  
  1         4  
18              
19 1     1   659 use IO::File;
  1         6922  
  1         83  
20 1     1   381 use FileHandle;
  1         707  
  1         4  
21 1     1   722 use File::Spec::Functions qw(splitpath file_name_is_absolute catfile catpath);
  1         707  
  1         63  
22 1     1   7 use File::Glob qw/:glob/;
  1         2  
  1         165  
23              
24              
25             # on debian with perl > 5.8.4 croak() doesn't work anymore without this.
26             # There is some require statement which dies 'cause it can't find Carp::Heavy,
27             # I really don't understand, what the hell they made, but the debian perl
28             # installation is definitely bullshit, damn!
29 1     1   357 use Carp::Heavy;
  1         117  
  1         25  
30              
31              
32 1     1   5 use Carp;
  1         2  
  1         36  
33 1     1   10 use Exporter;
  1         1  
  1         32  
34              
35             $Config::General::VERSION = "2.64";
36              
37 1     1   4 use vars qw(@ISA @EXPORT_OK);
  1         2  
  1         34  
38 1     1   5 use base qw(Exporter);
  1         2  
  1         153  
39             @EXPORT_OK = qw(ParseConfig SaveConfig SaveConfigString);
40              
41 1     1   6 use constant _UTF8_BOM => "\x{ef}\x{bb}\x{bf}";
  1         1  
  1         6750  
42              
43             sub new {
44             #
45             # create new Config::General object
46             #
47 71     71 1 29990 my($this, @param ) = @_;
48 71   66     229 my $class = ref($this) || $this;
49              
50             # define default options
51 71         966 my $self = {
52             # sha256 of current date
53             # hopefully this lowers the probability that
54             # this matches any configuration key or value out there
55             # bugfix for rt.40925
56             EOFseparator => 'ad7d7b87f5b81d2a0d5cb75294afeb91aa4801b1f8e8532dc1b633c0e1d47037',
57             SlashIsDirectory => 0,
58             AllowMultiOptions => 1,
59             MergeDuplicateOptions => 0,
60             MergeDuplicateBlocks => 0,
61             LowerCaseNames => 0,
62             ApacheCompatible => 0,
63             UseApacheInclude => 0,
64             IncludeRelative => 0,
65             IncludeDirectories => 0,
66             IncludeGlob => 0,
67             IncludeAgain => 0,
68             AutoLaunder => 0,
69             AutoTrue => 0,
70             AutoTrueFlags => {
71             true => '^(on|yes|true|1)$',
72             false => '^(off|no|false|0)$',
73             },
74             DefaultConfig => {},
75             String => '',
76             level => 1,
77             InterPolateVars => 0,
78             InterPolateEnv => 0,
79             ExtendedAccess => 0,
80             SplitPolicy => 'guess', # also possible: whitespace, equalsign and custom
81             SplitDelimiter => 0, # must be set by the user if SplitPolicy is 'custom'
82             StoreDelimiter => 0, # will be set by me unless user uses 'custom' policy
83             CComments => 1, # by default turned on
84             BackslashEscape => 0, # deprecated
85             StrictObjects => 1, # be strict on non-existent keys in OOP mode
86             StrictVars => 1, # be strict on undefined variables in Interpolate mode
87             Tie => q(), # could be set to a perl module for tie'ing new hashes
88             parsed => 0, # internal state stuff for variable interpolation
89             files => {}, # which files we have read, if any
90             UTF8 => 0,
91             SaveSorted => 0,
92             ForceArray => 0, # force single value array if value enclosed in []
93             AllowSingleQuoteInterpolation => 0,
94             NoEscape => 0,
95             NormalizeBlock => 0,
96             NormalizeOption => 0,
97             NormalizeValue => 0,
98             Plug => {},
99             UseApacheIfDefine => 0,
100             Define => {}
101             };
102              
103             # create the class instance
104 71         132 bless $self, $class;
105              
106 71 100       161 if ($#param >= 1) {
    100          
107             # use of the new hash interface!
108 61         123 $self->_prepare(@param);
109             }
110             elsif ($#param == 0) {
111             # use of the old style
112 9         19 $self->{ConfigFile} = $param[0];
113 9 50       18 if (ref($self->{ConfigFile}) eq 'HASH') {
114 0         0 $self->{ConfigHash} = delete $self->{ConfigFile};
115             }
116             }
117             else {
118             # this happens if $#param == -1,1 thus no param was given to new!
119 1         2 $self->{config} = $self->_hashref();
120 1         2 $self->{parsed} = 1;
121             }
122              
123             # find split policy to use for option/value separation
124 70         158 $self->_splitpolicy();
125              
126             # bless into variable interpolation module if necessary
127 70         124 $self->_blessvars();
128              
129             # process as usual
130 70 100       119 if (!$self->{parsed}) {
131 69         106 $self->_process();
132             }
133              
134 62 100       112 if ($self->{InterPolateVars}) {
135 11         27 $self->{config} = $self->_clean_stack($self->{config});
136             }
137              
138             # bless into OOP namespace if required
139 62         129 $self->_blessoop();
140              
141 62         186 return $self;
142             }
143              
144              
145              
146             sub _process {
147             #
148             # call _read() and _parse() on the given config
149 69     69   86 my($self) = @_;
150              
151 69 100 66     195 if ($self->{DefaultConfig} && $self->{InterPolateVars}) {
152 11         25 $self->{DefaultConfig} = $self->_interpolate_hash($self->{DefaultConfig}); # FIXME: _hashref() ?
153             }
154 69 100 33     229 if (exists $self->{StringContent}) {
    100          
    50          
155             # consider the supplied string as config file
156 14         34 $self->_read($self->{StringContent}, 'SCALAR');
157 14         27 $self->{config} = $self->_parse($self->{DefaultConfig}, $self->{content});
158             }
159             elsif (exists $self->{ConfigHash}) {
160 11 100       29 if (ref($self->{ConfigHash}) eq 'HASH') {
161             # initialize with given hash
162 10         14 $self->{config} = $self->{ConfigHash};
163 10         15 $self->{parsed} = 1;
164             }
165             else {
166 1         94 croak "Config::General: Parameter -ConfigHash must be a hash reference!\n";
167             }
168             }
169             elsif (ref($self->{ConfigFile}) eq 'GLOB' || ref($self->{ConfigFile}) eq 'FileHandle') {
170             # use the file the glob points to
171 0         0 $self->_read($self->{ConfigFile});
172 0         0 $self->{config} = $self->_parse($self->{DefaultConfig}, $self->{content});
173             }
174             else {
175 44 50       63 if ($self->{ConfigFile}) {
176             # open the file and read the contents in
177 44         77 $self->{configfile} = $self->{ConfigFile};
178 44 50       103 if ( file_name_is_absolute($self->{ConfigFile}) ) {
179             # look if this is an absolute path and save the basename if it is absolute
180 0         0 my ($volume, $path, undef) = splitpath($self->{ConfigFile});
181 0         0 $path =~ s#/$##; # remove eventually existing trailing slash
182 0 0       0 if (! $self->{ConfigPath}) {
183 0         0 $self->{ConfigPath} = [];
184             }
185 0         0 unshift @{$self->{ConfigPath}}, catpath($volume, $path, q());
  0         0  
186             }
187 44         284 $self->_open($self->{configfile});
188             # now, we parse immediately, getall simply returns the whole hash
189 41         136 $self->{config} = $self->_hashref();
190 41         91 $self->{config} = $self->_parse($self->{DefaultConfig}, $self->{content});
191             }
192             else {
193             # hm, no valid config file given, so try it as an empty object
194 0         0 $self->{config} = $self->_hashref();
195 0         0 $self->{parsed} = 1;
196             }
197             }
198             }
199              
200              
201             sub _blessoop {
202             #
203             # bless into ::Extended if necessary
204 62     62   79 my($self) = @_;
205 62 100       108 if ($self->{ExtendedAccess}) {
206             # we are blessing here again, to get into the ::Extended namespace
207             # for inheriting the methods available over there, which we doesn't have.
208 11         18 bless $self, 'Config::General::Extended';
209 11         12 eval {
210 11         716 require Config::General::Extended;
211             };
212 11 50       23 if ($EVAL_ERROR) {
213 0         0 croak "Config::General: " . $EVAL_ERROR;
214             }
215             }
216             # return $self;
217             }
218              
219             sub _blessvars {
220             #
221             # bless into ::Interpolated if necessary
222 70     70   97 my($self) = @_;
223 70 100 66     206 if ($self->{InterPolateVars} || $self->{InterPolateEnv}) {
224             # InterPolateEnv implies InterPolateVars
225 11         15 $self->{InterPolateVars} = 1;
226              
227             # we are blessing here again, to get into the ::InterPolated namespace
228             # for inheriting the methods available overthere, which we doesn't have here.
229 11         81 bless $self, 'Config::General::Interpolated';
230 11         16 eval {
231 11         670 require Config::General::Interpolated;
232             };
233 11 50       22 if ($EVAL_ERROR) {
234 0         0 croak "Config::General: " . $EVAL_ERROR;
235             }
236             # pre-compile the variable regexp
237 11         23 $self->{regex} = $self->_set_regex();
238             }
239             }
240              
241              
242             sub _splitpolicy {
243             #
244             # find out what split policy to use
245 70     70   110 my($self) = @_;
246 70 100       114 if ($self->{SplitPolicy} ne 'guess') {
247 2 50       8 if ($self->{SplitPolicy} eq 'whitespace') {
    50          
    50          
248 0         0 $self->{SplitDelimiter} = '\s+';
249 0 0       0 if (!$self->{StoreDelimiter}) {
250 0         0 $self->{StoreDelimiter} = q( );
251             }
252             }
253             elsif ($self->{SplitPolicy} eq 'equalsign') {
254 0         0 $self->{SplitDelimiter} = '\s*=\s*';
255 0 0       0 if (!$self->{StoreDelimiter}) {
256 0         0 $self->{StoreDelimiter} = ' = ';
257             }
258             }
259             elsif ($self->{SplitPolicy} eq 'custom') {
260 2 50       5 if (! $self->{SplitDelimiter} ) {
261 0         0 croak "Config::General: SplitPolicy set to 'custom' but no SplitDelimiter set.\n";
262             }
263             }
264             else {
265 0         0 croak "Config::General: Unsupported SplitPolicy: $self->{SplitPolicy}.\n";
266             }
267             }
268             else {
269 68 50       116 if (!$self->{StoreDelimiter}) {
270 68         109 $self->{StoreDelimiter} = q( );
271             }
272             }
273             }
274              
275             sub _prepare {
276             #
277             # prepare the class parameters, mangle them, if there
278             # are options to reset or to override, do it here.
279 61     61   174 my ($self, %conf) = @_;
280              
281             # save the parameter list for ::Extended's new() calls
282 61         113 $self->{Params} = \%conf;
283              
284             # be backwards compatible
285 61 100       120 if (exists $conf{-file}) {
286 6         12 $self->{ConfigFile} = delete $conf{-file};
287             }
288 61 50       88 if (exists $conf{-hash}) {
289 0         0 $self->{ConfigHash} = delete $conf{-hash};
290             }
291              
292             # store input, file, handle, or array
293 61 100       89 if (exists $conf{-ConfigFile}) {
294 29         52 $self->{ConfigFile} = delete $conf{-ConfigFile};
295             }
296 61 100       98 if (exists $conf{-ConfigHash}) {
297 11         20 $self->{ConfigHash} = delete $conf{-ConfigHash};
298             }
299              
300             # store search path for relative configs, if any
301 61 50       84 if (exists $conf{-ConfigPath}) {
302 0         0 my $configpath = delete $conf{-ConfigPath};
303 0 0       0 $self->{ConfigPath} = ref $configpath eq 'ARRAY' ? $configpath : [$configpath];
304             }
305              
306             # handle options which contains values we need (strings, hashrefs or the like)
307 61 100       95 if (exists $conf{-String} ) {
308 15 100       30 if (not ref $conf{-String}) {
    100          
309 13 50       19 if ( $conf{-String}) {
310 13         19 $self->{StringContent} = $conf{-String};
311             }
312 13         20 delete $conf{-String};
313             }
314             # re-implement arrayref support, removed after 2.22 as _read were
315             # re-organized
316             # fixed bug#33385
317             elsif(ref($conf{-String}) eq 'ARRAY') {
318 1         2 $self->{StringContent} = join "\n", @{$conf{-String}};
  1         3  
319             }
320             else {
321 1         109 croak "Config::General: Parameter -String must be a SCALAR or ARRAYREF!\n";
322             }
323 14         16 delete $conf{-String};
324             }
325 60 100       87 if (exists $conf{-Tie}) {
326 1 50       3 if ($conf{-Tie}) {
327 1         2 $self->{Tie} = delete $conf{-Tie};
328 1         3 $self->{DefaultConfig} = $self->_hashref();
329             }
330             }
331              
332 60 100       91 if (exists $conf{-FlagBits}) {
333 2 50 33     9 if ($conf{-FlagBits} && ref($conf{-FlagBits}) eq 'HASH') {
334 2         4 $self->{FlagBits} = 1;
335 2         13 $self->{FlagBitsFlags} = $conf{-FlagBits};
336             }
337 2         4 delete $conf{-FlagBits};
338             }
339              
340 60 100       89 if (exists $conf{-DefaultConfig}) {
341 3 100 66     17 if ($conf{-DefaultConfig} && ref($conf{-DefaultConfig}) eq 'HASH') {
    50 33        
342             # copy the hashref so that it is not being modified by subsequent calls, fixes bug#142095
343 2         5 $self->{DefaultConfig} = $self->_copy($conf{-DefaultConfig});
344             }
345             elsif ($conf{-DefaultConfig} && ref($conf{-DefaultConfig}) eq q()) {
346 1         4 $self->_read($conf{-DefaultConfig}, 'SCALAR');
347 1         3 $self->{DefaultConfig} = $self->_parse($self->_hashref(), $self->{content});
348 1         2 $self->{content} = ();
349             }
350 3         4 delete $conf{-DefaultConfig};
351             }
352              
353             # handle options which may either be true or false
354             # allowing "human" logic about what is true and what is not
355 60         141 foreach my $entry (keys %conf) {
356 75         105 my $key = $entry;
357 75         220 $key =~ s/^\-//;
358 75 50       150 if (! exists $self->{$key}) {
359 0         0 croak "Config::General: Unknown parameter: $entry => \"$conf{$entry}\" (key: <$key>)\n";
360             }
361 75 100       299 if ($conf{$entry} =~ /$self->{AutoTrueFlags}->{true}/io) {
    100          
362 61         143 $self->{$key} = 1;
363             }
364             elsif ($conf{$entry} =~ /$self->{AutoTrueFlags}->{false}/io) {
365 6         14 $self->{$key} = 0;
366             }
367             else {
368             # keep it untouched
369 8         17 $self->{$key} = $conf{$entry};
370             }
371             }
372              
373 60 100       111 if ($self->{MergeDuplicateOptions}) {
374             # override if not set by user
375 6 50       12 if (! exists $conf{-AllowMultiOptions}) {
376 6         8 $self->{AllowMultiOptions} = 0;
377             }
378             }
379              
380 60 50       96 if ($self->{ApacheCompatible}) {
381             # turn on all apache compatibility options which has
382             # been incorporated during the years...
383 0         0 $self->{UseApacheInclude} = 1;
384 0         0 $self->{IncludeRelative} = 1;
385 0         0 $self->{IncludeDirectories} = 1;
386 0         0 $self->{IncludeGlob} = 1;
387 0         0 $self->{SlashIsDirectory} = 1;
388 0         0 $self->{SplitPolicy} = 'whitespace';
389 0         0 $self->{CComments} = 0;
390 0         0 $self->{UseApacheIfDefine} = 1;
391             }
392              
393 60 100       119 if ($self->{UseApacheIfDefine}) {
394 3 50       6 if (exists $conf{-Define}) {
395 3         7 my $ref = ref($conf{-Define});
396              
397 3 100       12 if ($ref eq '') {
    50          
    100          
    50          
398 1         3 $self->{Define} = {$conf{-Define} => 1};
399             }
400             elsif ($ref eq 'SCALAR') {
401 0         0 $self->{Define} = {${$conf{-Define}} = 1};
  0         0  
402             }
403             elsif ($ref eq 'ARRAY') {
404 1         1 my %h = map { $_ => 1 } @{$conf{-Define}};
  1         4  
  1         3  
405 1         2 $self->{Define} = \%h;
406             }
407             elsif ($ref eq 'HASH') {
408 1         2 $self->{Define} = $conf{-Define};
409             }
410             else {
411 0         0 croak qq{Config::General: Unsupported ref '$ref' for 'Define'};
412             }
413              
414 3         6 delete $conf{-Define};
415             }
416              
417             }
418             }
419              
420             sub getall {
421             #
422             # just return the whole config hash
423             #
424 49     49 1 408 my($this) = @_;
425 49 50       70 return (exists $this->{config} ? %{$this->{config}} : () );
  49         280  
426             }
427              
428              
429             sub files {
430             #
431             # return a list of files opened so far
432             #
433 1     1 1 4 my($this) = @_;
434 1 50       4 return (exists $this->{files} ? keys %{$this->{files}} : () );
  1         4  
435             }
436              
437              
438             sub _open {
439             #
440             # open the config file, or expand a directory or glob or include
441             #
442 71     71   117 my($this, $basefile, $basepath) = @_;
443 71         70 my $cont;
444              
445 71         119 ($cont, $basefile, $basepath) = $this->_hook('pre_open', $basefile, $basepath);
446 71 50       117 return if(!$cont);
447              
448 71         96 my($fh, $configfile);
449              
450 71 100       110 if($basepath) {
451             # if this doesn't work we can still try later the global config path to use
452 8         33 $configfile = catfile($basepath, $basefile);
453             }
454             else {
455 63         69 $configfile = $basefile;
456             }
457              
458 71         241 my $glob = qr/[*?\[\{\\]/;
459 71 50       228 if ($^O =~ /win/i) {
460             # fix for rt.cpan.org#116340: do only consider a backslash
461             # as meta escape char, but not if it appears on it's own,
462             # as it happens on windows platforms.
463 0         0 $glob = qr/(\\[*?\[\{\\]|[*?\[\{])/;
464             }
465              
466 71 100 100     184 if ($this->{IncludeGlob} and $configfile =~ /$glob/) {
467             # Something like: *.conf (or maybe dir/*.conf) was included; expand it and
468             # pass each expansion through this method again.
469 4         6 local $_;
470 4         343 my @include = grep { -f $_ } bsd_glob($configfile, GLOB_BRACE | GLOB_QUOTE);
  6         70  
471              
472             # applied patch by AlexK fixing rt.cpan.org#41030
473 4 50 66     20 if ( !@include && defined $this->{ConfigPath} ) {
474 0         0 foreach my $dir (@{$this->{ConfigPath}}) {
  0         0  
475 0         0 my ($volume, $path, undef) = splitpath($basefile);
476 0 0       0 if ( -d catfile( $dir, $path ) ) {
477 0         0 push @include, grep { -f $_ } bsd_glob(catfile($dir, $basefile), GLOB_BRACE | GLOB_QUOTE);
  0         0  
478 0         0 last;
479             }
480             }
481             }
482              
483             # Multiple results or no expansion results (which is fine,
484             # include foo/* shouldn't fail if there isn't anything matching)
485             # rt.cpan.org#79869: local $this->{IncludeGlob};
486 4         8 foreach my $file (@include) {
487 6         16 $this->_open($file);
488             }
489 4         17 return;
490             }
491              
492 67 100       918 if (!-e $configfile) {
493 3         8 my $found;
494 3 50       8 if (defined $this->{ConfigPath}) {
495             # try to find the file within ConfigPath
496 0         0 foreach my $dir (@{$this->{ConfigPath}}) {
  0         0  
497 0 0       0 if( -e catfile($dir, $basefile) ) {
498 0         0 $configfile = catfile($dir, $basefile);
499 0         0 $found = 1;
500 0         0 last; # found it
501             }
502             }
503             }
504 3 50       4 if (!$found) {
505 3 50       8 my $path_message = defined $this->{ConfigPath} ? q( within ConfigPath: ) . join(q(.), @{$this->{ConfigPath}}) : q();
  0         0  
506 3         428 croak qq{Config::General The file "$basefile" does not exist$path_message!};
507             }
508             }
509              
510 64         353 local ($RS) = $RS;
511 64 50       146 if (! $RS) {
512 0         0 carp(q(\$RS (INPUT_RECORD_SEPARATOR) is undefined. Guessing you want a line feed character));
513 0         0 $RS = "\n";
514             }
515              
516 64 100 66     1034 if (-d $configfile and $this->{IncludeDirectories}) {
    50          
    50          
517             # A directory was included; include all the files inside that directory in ASCII order
518 1         4 local *INCLUDEDIR;
519 1 50       38 opendir INCLUDEDIR, $configfile or croak "Config::General: Could not open directory $configfile!($!)\n";
520 1         34 my @files = sort grep { -f catfile($configfile, $_) } catfile($configfile, $_), readdir INCLUDEDIR;
  9         118  
521 1         15 closedir INCLUDEDIR;
522 1         4 local $this->{CurrentConfigFilePath} = $configfile;
523 1         3 for (@files) {
524 5         20 my $file = catfile($configfile, $_);
525 5 50 33     16 if (! exists $this->{files}->{$file} or $this->{IncludeAgain} ) {
526             # support re-read if used urged us to do so, otherwise ignore the file
527 5         10 $fh = $this->_openfile_for_read($file);
528 5         17 $this->{files}->{"$file"} = 1;
529 5         15 $this->_read($fh);
530             }
531             else {
532 0         0 warn "File $file already loaded. Use -IncludeAgain to load it again.\n";
533             }
534             }
535             }
536             elsif (-d $configfile) {
537 0         0 croak "Config::General: config file argument is a directory, expecting a file!\n";
538             }
539             elsif (-e _) {
540 63 100 100     213 if (exists $this->{files}->{$configfile} and not $this->{IncludeAgain}) {
541             # do not read the same file twice, just return
542 1         10 warn "File $configfile already loaded. Use -IncludeAgain to load it again.\n";
543 1         9 return;
544             }
545             else {
546 62         137 $fh = $this->_openfile_for_read($configfile);
547 62         148 $this->{files}->{$configfile} = 1;
548              
549 62         168 my ($volume, $path, undef) = splitpath($configfile);
550 62         800 local $this->{CurrentConfigFilePath} = catpath($volume, $path, q());
551              
552 62         422 $this->_read($fh);
553             }
554             }
555 62         923 return;
556             }
557              
558              
559             sub _openfile_for_read {
560             #
561             # actually open a file, turn on utf8 mode if requested by bom
562             #
563 67     67   120 my ($this, $file) = @_;
564              
565 67 50       288 my $fh = IO::File->new( $file, 'r')
566             or croak "Config::General: Could not open $file!($!)\n";
567              
568             # attempt to read an initial utf8 byte-order mark (BOM)
569 67         5920 my $n_read = sysread $fh, my $read_BOM, length(_UTF8_BOM);
570 67   66     310 my $has_BOM = $n_read == length(_UTF8_BOM) && $read_BOM eq _UTF8_BOM;
571              
572             # set utf8 perlio layer if BOM was found or if option -UTF8 is turned on
573 67 100 100     221 binmode $fh, ":utf8" if $this->{UTF8} || $has_BOM;
574              
575             # rewind to beginning of file if we read chars that were not the BOM
576 67 100 66     434 sysseek $fh, 0, 0 if $n_read && !$has_BOM;
577              
578 67         242 return $fh;
579             }
580              
581              
582              
583             sub _read {
584             #
585             # store the config contents in @content
586             # and prepare it somewhat for easier parsing later
587             # (comments, continuing lines, and stuff)
588             #
589 82     82   126 my($this, $fh, $flag) = @_;
590              
591              
592 82         92 my(@stuff, @content, $c_comment, $longline, $hier, $hierend, @hierdoc);
593 82         116 local $_ = q();
594              
595 82 100 66     177 if ($flag && $flag eq 'SCALAR') {
596 15 50       22 if (ref($fh) eq 'ARRAY') {
597 0         0 @stuff = @{$fh};
  0         0  
598             }
599             else {
600 15         83 @stuff = split /\n/, $fh;
601             }
602             }
603             else {
604 67         1630 @stuff = <$fh>;
605             }
606              
607 82         210 my $cont;
608 82         180 ($cont, $fh, @stuff) = $this->_hook('pre_read', $fh, @stuff);
609 82 50       148 return if(!$cont);
610              
611 82 100       137 if ($this->{UseApacheIfDefine}) {
612 3         7 $this->_process_apache_ifdefine(\@stuff);
613             }
614              
615 82         139 foreach (@stuff) {
616 766 50       1167 if ($this->{AutoLaunder}) {
617 0 0       0 if (m/^(.*)$/) {
618 0         0 $_ = $1;
619             }
620             }
621              
622 766         836 chomp;
623              
624              
625 766 100       989 if ($hier) {
626             # inside here-doc, only look for $hierend marker
627 25 100       89 if (/^(\s*)\Q$hierend\E\s*$/) {
628 7         9 my $indent = $1; # preserve indentation
629 7         15 $hier .= ' ' . $this->{EOFseparator}; # bugfix of rt.40925
630             # _parse will also preserver indentation
631 7 50       20 if ($indent) {
632 0         0 foreach (@hierdoc) {
633 0         0 s/^$indent//; # i.e. the end was: " EOF" then we remove " " from every here-doc line
634 0         0 $hier .= $_ . "\n"; # and store it in $hier
635             }
636             }
637             else {
638 7         29 $hier .= join "\n", @hierdoc; # there was no indentation of the end-string, so join it 1:1
639             }
640 7         8 push @{$this->{content}}, $hier; # push it onto the content stack
  7         20  
641 7         12 @hierdoc = ();
642 7         8 undef $hier;
643 7         9 undef $hierend;
644             }
645             else {
646             # everything else onto the stack
647 18         25 push @hierdoc, $_;
648             }
649 25         39 next;
650             }
651              
652 741 50       1022 if ($this->{CComments}) {
653             # look for C-Style comments, if activated
654 741 100       1614 if (/(\s*\/\*.*\*\/\s*)/) {
    100          
    100          
655             # single c-comment on one line
656 2         6 s/\s*\/\*.*\*\/\s*//;
657             }
658             elsif (/^\s*\/\*/) {
659             # the beginning of a C-comment ("/*"), from now on ignore everything.
660 15 50       26 if (/\*\/\s*$/) {
661             # C-comment end is already there, so just ignore this line!
662 0         0 $c_comment = 0;
663             }
664             else {
665 15         16 $c_comment = 1;
666             }
667             }
668             elsif (/\*\//) {
669 15 50       21 if (!$c_comment) {
670 0         0 warn "invalid syntax: found end of C-comment without previous start!\n";
671             }
672 15         17 $c_comment = 0; # the current C-comment ends here, go on
673 15         38 s/^.*\*\///; # if there is still stuff, it will be read
674             }
675 741 100       1004 next if($c_comment); # ignore EVERYTHING from now on, IF it IS a C-Comment
676             }
677              
678             # Remove comments and empty lines
679 589         799 s/(? .* bugfix rt.cpan.org#44600
680 589 50       828 next if /^\s*#/;
681              
682             # look for multiline option, indicated by a trailing backslash
683 589 100       787 if (/(?
684 9         17 chop; # remove trailing backslash
685 9         19 s/^\s*//;
686 9         16 $longline .= $_;
687 9         11 next;
688             }
689              
690             # transform explicit-empty blocks to conforming blocks
691             # rt.cpan.org#80006 added \s* before $/
692 580 100 66     1439 if (!$this->{ApacheCompatible} && /\s*<([^\/]+?.*?)\/>\s*$/) {
693 3         9 my $block = $1;
694 3 50       7 if ($block !~ /\"/) {
695 3 50       6 if ($block !~ /\s[^\s]/) {
696             # fix of bug 7957, add quotation to pure slash at the
697             # end of a block so that it will be considered as directory
698             # unless the block is already quoted or contains whitespaces
699             # and no quotes.
700 3 50       7 if ($this->{SlashIsDirectory}) {
701 3         4 push @{$this->{content}}, '<' . $block . '"/">';
  3         9  
702 3         6 next;
703             }
704             }
705             }
706 0         0 my $orig = $_;
707 0         0 $orig =~ s/\/>$/>/;
708 0         0 $block =~ s/\s\s*.*$//;
709 0         0 push @{$this->{content}}, $orig, "";
  0         0  
710 0         0 next;
711             }
712              
713              
714             # look for here-doc identifier
715 577 100       798 if ($this->{SplitPolicy} eq 'guess') {
716 507 100       943 if (/^\s*([^=]+?)\s*=\s*<<\s*(.+?)\s*$/) {
    100          
717             # try equal sign (fix bug rt#36607)
718 2         5 $hier = $1; # the actual here-doc variable name
719 2         4 $hierend = $2; # the here-doc identifier, i.e. "EOF"
720 2         2 next;
721             }
722             elsif (/^\s*(\S+?)\s+<<\s*(.+?)\s*$/) {
723             # try whitespace
724 5         11 $hier = $1; # the actual here-doc variable name
725 5         7 $hierend = $2; # the here-doc identifier, i.e. "EOF"
726 5         9 next;
727             }
728             }
729             else {
730             # no guess, use one of the configured strict split policies
731 70 100       251 if (/^\s*(.+?)($this->{SplitDelimiter})<<\s*(.+?)\s*$/) {
732 1         4 $hier = $1; # the actual here-doc variable name
733 1         2 $hierend = $3; # the here-doc identifier, i.e. "EOF"
734 1         2 next;
735             }
736             }
737              
738              
739              
740             ###
741             ### any "normal" config lines from now on
742             ###
743              
744 569 100       705 if ($longline) {
745             # previous stuff was a longline and this is the last line of the longline
746 5         12 s/^\s*//;
747 5         13 $longline .= $_;
748 5         10 push @{$this->{content}}, $longline; # push it onto the content stack
  5         10  
749 5         10 undef $longline;
750 5         7 next;
751             }
752             else {
753             # ignore empty lines
754 564 100       1067 next if /^\s*$/;
755              
756             # look for include statement(s)
757 433         455 my $incl_file;
758 433         507 my $path = '';
759 433 100 66     917 if ( $this->{IncludeRelative} and defined $this->{CurrentConfigFilePath}) {
    50          
760 87         110 $path = $this->{CurrentConfigFilePath};
761             }
762             elsif (defined $this->{ConfigPath}) {
763             # fetch pathname of base config file, assuming the 1st one is the path of it
764 0         0 $path = $this->{ConfigPath}->[0];
765             }
766              
767             # bugfix rt.cpan.org#38635: support quoted filenames
768 433 100       586 if ($this->{UseApacheInclude}) {
769 16         21 my $opt = '';
770 16 100       61 if (/^\s*(include|includeoptional)\s*(["'])(.*?)(?
    100          
771 1         4 $incl_file = $3;
772 1         2 $opt = $1;
773             }
774             elsif (/^\s*(include|includeoptional)\s+(.+?)\s*$/i) {
775 3         8 $incl_file = $2;
776 3         6 $opt = $1;
777             }
778 16 100       27 if ($incl_file) {
779 4 50 66     20 if ($this->{IncludeGlob} && $opt =~ /opt/i && $incl_file !~ /[*?\[\{\\]/) {
      66        
780             # fix rt#107108
781             # glob enabled && optional include && file is not already a glob:
782             # turn it into a singular matching glob, like:
783             # "file" => "[f][i][l][e]" and:
784             # "dir/file" => "dir/[f][i][l][e]"
785             # which IS a glob but only matches that particular file. if it
786             # doesn't exist, it will be ignored by _open(), just what
787             # we'd like to have when using IncludeOptional.
788 2         7 my ($vol,$dirs,$file) = splitpath( $incl_file );
789 2         26 $incl_file = catpath($vol, $dirs, join '', map { "[$_]" } split //, $file);
  29         45  
790             }
791             }
792             }
793             else {
794 417 50       756 if (/^\s*<>\\s*$/i) {
    100          
795 0         0 $incl_file = $2;
796             }
797             elsif (/^\s*<>\s*$/i) {
798 17         39 $incl_file = $1;
799             }
800             }
801              
802 433 100       531 if ($incl_file) {
803 21 100 66     64 if ( $this->{IncludeRelative} && $path && !file_name_is_absolute($incl_file) ) {
      66        
804             # include the file from within location of $this->{configfile}
805 8         52 $this->_open( $incl_file, $path );
806             }
807             else {
808             # include the file from within pwd, or absolute
809 13         55 $this->_open($incl_file);
810             }
811             }
812             else {
813             # standard entry, (option = value)
814 412         435 push @{$this->{content}}, $_;
  412         827  
815             }
816              
817             }
818              
819             }
820              
821 81         164 ($cont, $this->{content}) = $this->_hook('post_read', $this->{content});
822 81         258 return 1;
823             }
824              
825              
826             sub _process_apache_ifdefine {
827             #
828             # Loop trough config lines and exclude all those entries
829             # for which there's no IFDEF but which reside inside an IFDEF.
830             #
831             # Called from _read(), if UseApacheIfDefine is enabled, returns
832             # the modified array.
833 3     3   6 my($this, $rawlines) = @_;
834              
835 3         3 my @filtered;
836 3         4 my $includeFlag = 1;
837 3         4 my $blockLevel = 0;
838              
839 3         4 foreach (@{$rawlines}) {
  3         7  
840 9 100 33     50 if (/^<\s*IfDefine\s+([!]*)("[^"]+"|\S+)\s*>/i) {
    100          
    50          
    50          
841             # new IFDEF block, mark following content to be included if
842             # the DEF is known, otherwise skip it til end of IFDEF
843 3         10 my ($negate, $define) = ($1 eq '!',$2);
844              
845 3         4 $blockLevel++;
846 3         7 $includeFlag &= ((not $negate) & (exists $this->{Define}{$define}));
847             } elsif (/^<\s*\/IfDefine\s*>/i) {
848 3         4 $blockLevel--;
849 3         6 $includeFlag = $blockLevel == 0;
850             } elsif ($includeFlag && /\s*Define\s+("[^"]+"|\S+)/i) {
851             # inline Define, add it to our list
852 0         0 $this->{Define}{$1} = 1;
853             } elsif ($includeFlag) {
854 3         7 push @filtered, $_;
855             }
856             }
857              
858 3 50       6 if ($blockLevel) {
859 0         0 croak qq(Config::General: Block has no EndBlock statement!\n);
860             }
861              
862 3         7 @$rawlines = @filtered; # replace caller array
863             }
864              
865              
866             sub _parse {
867             #
868             # parse the contents of the file
869             #
870 138     138   200 my($this, $config, $content) = @_;
871 138         160 my(@newcontent, $block, $blockname, $chunk,$block_level);
872 138         155 local $_;
873              
874 138         144 foreach (@{$content}) { # loop over content stack
  138         208  
875 621         786 chomp;
876 621         647 $chunk++;
877 621         1136 $_ =~ s/^\s+//; # strip spaces @ end and begin
878 621         1230 $_ =~ s/\s+$//;
879              
880             #
881             # build option value assignment, split current input
882             # using whitespace, equal sign or optionally here-doc
883             # separator EOFseparator
884 621         664 my ($option,$value);
885 621 100       1160 if (/$this->{EOFseparator}/) {
886 7         50 ($option,$value) = split /\s*$this->{EOFseparator}\s*/, $_, 2; # separated by heredoc-finding in _open()
887             }
888             else {
889 614 100       863 if ($this->{SplitPolicy} eq 'guess') {
890             # again the old regex. use equalsign SplitPolicy to get the
891             # 2.00 behavior. the new regexes were too odd.
892 499         1670 ($option,$value) = split /\s*=\s*|\s+/, $_, 2;
893             }
894             else {
895             # no guess, use one of the configured strict split policies
896 115         346 ($option,$value) = split /$this->{SplitDelimiter}/, $_, 2;
897             }
898             }
899              
900 621 50       1033 if($this->{NormalizeOption}) {
901 0         0 $option = $this->{NormalizeOption}($option);
902             }
903              
904 621 100 100     1472 if ($value && $value =~ /^"/ && $value =~ /"$/) {
      100        
905 9         22 $value =~ s/^"//; # remove leading and trailing "
906 9         22 $value =~ s/"$//;
907             }
908 621 100       1233 if (! defined $block) { # not inside a block @ the moment
    100          
    100          
909 335 100       675 if (/^<([^\/]+?.*?)>$/) { # look if it is a block
    50          
910 86         167 $block = $1; # store block name
911 86 100       122 if ($block =~ /^"([^"]+)"$/) {
912             # quoted block, unquote it and do not split
913 1         3 $block =~ s/"//g;
914             }
915             else {
916             # If it is a named block store the name separately; allow the block and name to each be quoted
917 85 50       325 if ($block =~ /^(?:"([^"]+)"|(\S+))(?:\s+(?:"([^"]+)"|(.*)))?$/) {
918 85   66     272 $block = $1 || $2;
919 85   100     209 $blockname = $3 || $4;
920             }
921             }
922 86 50       140 if($this->{NormalizeBlock}) {
923 0         0 $block = $this->{NormalizeBlock}($block);
924 0 0       0 if (defined $blockname) {
925 0         0 $blockname = $this->{NormalizeBlock}($blockname);
926 0 0       0 if($blockname eq "") {
927             # if, after normalization no blockname is left, remove it
928 0         0 $blockname = undef;
929             }
930             }
931             }
932 86 100       131 if ($this->{InterPolateVars}) {
933             # interpolate block(name), add "<" and ">" to the key, because
934             # it is sure that such keys does not exist otherwise.
935 30         88 $block = $this->_interpolate($config, "<$block>", $block);
936 30 100       57 if (defined $blockname) {
937 12         32 $blockname = $this->_interpolate($config, "<$blockname>", "$blockname");
938             }
939             }
940 86 50       137 if ($this->{LowerCaseNames}) {
941 0         0 $block = lc $block; # only for blocks lc(), if configured via new()
942             }
943 86         99 $this->{level} += 1;
944 86         123 undef @newcontent;
945 86         124 next;
946             }
947             elsif (/^<\/(.+?)>$/) { # it is an end block, but we don't have a matching block!
948 0         0 croak "Config::General: EndBlock \"<\/$1>\" has no StartBlock statement (level: $this->{level}, chunk $chunk)!\n";
949             }
950             else { # insert key/value pair into actual node
951 249 50       351 if ($this->{LowerCaseNames}) {
952 0         0 $option = lc $option;
953             }
954              
955 249 100       334 if (exists $config->{$option}) {
956 25 100       39 if ($this->{MergeDuplicateOptions}) {
957 5         8 $config->{$option} = $this->_parse_value($config, $option, $value);
958              
959             # bugfix rt.cpan.org#33216
960 5 100       11 if ($this->{InterPolateVars}) {
961             # save pair on local stack
962 2         5 $config->{__stack}->{$option} = $config->{$option};
963             }
964             }
965             else {
966 20 50       32 if (! $this->{AllowMultiOptions} ) {
967             # no, duplicates not allowed
968 0         0 croak "Config::General: Option \"$option\" occurs more than once (level: $this->{level}, chunk $chunk)!\n";
969             }
970             else {
971             # yes, duplicates allowed
972 20 100       37 if (ref($config->{$option}) ne 'ARRAY') { # convert scalar to array
973 13         18 my $savevalue = $config->{$option};
974 13         18 delete $config->{$option};
975 13         15 push @{$config->{$option}}, $savevalue;
  13         29  
976             }
977 20         28 eval {
978             # check if arrays are supported by the underlying hash
979 20         20 my $i = scalar @{$config->{$option}};
  20         29  
980             };
981 20 50       26 if ($EVAL_ERROR) {
982 0         0 $config->{$option} = $this->_parse_value($config, $option, $value);
983             }
984             else {
985             # it's already an array, just push
986 20         21 push @{$config->{$option}}, $this->_parse_value($config, $option, $value);
  20         38  
987             }
988             }
989             }
990             }
991             else {
992 224 100 66     456 if($this->{ForceArray} && defined $value && $value =~ /^\[\s*(.+?)\s*\]$/) {
      66        
993             # force single value array entry
994 2         3 push @{$config->{$option}}, $this->_parse_value($config, $option, $1);
  2         6  
995             }
996             else {
997             # standard config option, insert key/value pair into node
998 222         373 $config->{$option} = $this->_parse_value($config, $option, $value);
999              
1000 222 100       443 if ($this->{InterPolateVars}) {
1001             # save pair on local stack
1002 72         160 $config->{__stack}->{$option} = $config->{$option};
1003             }
1004             }
1005             }
1006             }
1007             }
1008             elsif (/^<([^\/]+?.*?)>$/) { # found a start block inside a block, don't forget it
1009 21         26 $block_level++; # $block_level indicates wether we are still inside a node
1010 21         35 push @newcontent, $_; # push onto new content stack for later recursive call of _parse()
1011             }
1012             elsif (/^<\/(.+?)>$/) {
1013 107 100       164 if ($block_level) { # this endblock is not the one we are searching for, decrement and push
1014 21         23 $block_level--; # if it is 0, then the endblock was the one we searched for, see below
1015 21         35 push @newcontent, $_; # push onto new content stack
1016             }
1017             else { # calling myself recursively, end of $block reached, $block_level is 0
1018 86 100       121 if (defined $blockname) {
1019             # a named block, make it a hashref inside a hash within the current node
1020              
1021 42 100       77 if (! exists $config->{$block}) {
1022             # Make sure that the hash is not created implicitly
1023 30         55 $config->{$block} = $this->_hashref();
1024              
1025 30 100       53 if ($this->{InterPolateVars}) {
1026             # inherit current __stack to new block
1027 9         18 $config->{$block}->{__stack} = $this->_copy($config->{__stack});
1028             }
1029             }
1030              
1031 42 100       117 if (ref($config->{$block}) eq '') {
    100          
    50          
1032 1         99 croak "Config::General: Block <$block> already exists as scalar entry!\n";
1033             }
1034             elsif (ref($config->{$block}) eq 'ARRAY') {
1035 2         220 croak "Config::General: Cannot append named block <$block $blockname> to array of scalars!\n"
1036             ."Block <$block> or scalar '$block' occurs more than once.\n"
1037             ."Turn on -MergeDuplicateBlocks or make sure <$block> occurs only once in the config.\n";
1038             }
1039             elsif (exists $config->{$block}->{$blockname}) {
1040             # the named block already exists, make it an array
1041 0 0       0 if ($this->{MergeDuplicateBlocks}) {
1042             # just merge the new block with the same name as an existing one into
1043             # this one.
1044 0         0 $config->{$block}->{$blockname} = $this->_parse($config->{$block}->{$blockname}, \@newcontent);
1045             }
1046             else {
1047 0 0       0 if (! $this->{AllowMultiOptions}) {
1048 0         0 croak "Config::General: Named block \"<$block $blockname>\" occurs more than once (level: $this->{level}, chunk $chunk)!\n";
1049             }
1050             else { # preserve existing data
1051 0         0 my $savevalue = $config->{$block}->{$blockname};
1052 0         0 delete $config->{$block}->{$blockname};
1053 0         0 my @ar;
1054 0 0       0 if (ref $savevalue eq 'ARRAY') {
1055 0         0 push @ar, @{$savevalue}; # preserve array if any
  0         0  
1056             }
1057             else {
1058 0         0 push @ar, $savevalue;
1059             }
1060 0         0 push @ar, $this->_parse( $this->_hashref(), \@newcontent); # append it
1061 0         0 $config->{$block}->{$blockname} = \@ar;
1062             }
1063             }
1064             }
1065             else {
1066             # the first occurrence of this particular named block
1067 39         62 my $tmphash = $this->_hashref();
1068              
1069 39 100       71 if ($this->{InterPolateVars}) {
1070             # inherit current __stack to new block
1071 12         17 $tmphash->{__stack} = $this->_copy($config->{__stack});
1072             }
1073              
1074 39         87 $config->{$block}->{$blockname} = $this->_parse($tmphash, \@newcontent);
1075             }
1076             }
1077             else {
1078             # standard block
1079 44 100       65 if (exists $config->{$block}) {
1080 5 100       13 if (ref($config->{$block}) eq '') {
1081 1         102 croak "Config::General: Cannot create hashref from <$block> because there is\n"
1082             ."already a scalar option '$block' with value '$config->{$block}'\n";
1083             }
1084              
1085             # the block already exists, make it an array
1086 4 100       7 if ($this->{MergeDuplicateBlocks}) {
1087             # just merge the new block with the same name as an existing one into
1088             # this one.
1089 2         7 $config->{$block} = $this->_parse($config->{$block}, \@newcontent);
1090             }
1091             else {
1092 2 50       5 if (! $this->{AllowMultiOptions}) {
1093 0         0 croak "Config::General: Block \"<$block>\" occurs more than once (level: $this->{level}, chunk $chunk)!\n";
1094             }
1095             else {
1096 2         3 my $savevalue = $config->{$block};
1097 2         4 delete $config->{$block};
1098 2         2 my @ar;
1099 2 50       4 if (ref $savevalue eq "ARRAY") {
1100 0         0 push @ar, @{$savevalue};
  0         0  
1101             }
1102             else {
1103 2         3 push @ar, $savevalue;
1104             }
1105              
1106             # fixes rt#31529
1107 2         4 my $tmphash = $this->_hashref();
1108 2 50       5 if ($this->{InterPolateVars}) {
1109             # inherit current __stack to new block
1110 0         0 $tmphash->{__stack} = $this->_copy($config->{__stack});
1111             }
1112              
1113 2         5 push @ar, $this->_parse( $tmphash, \@newcontent);
1114              
1115 2         7 $config->{$block} = \@ar;
1116             }
1117             }
1118             }
1119             else {
1120             # the first occurrence of this particular block
1121 39         74 my $tmphash = $this->_hashref();
1122              
1123 39 100       70 if ($this->{InterPolateVars}) {
1124             # inherit current __stack to new block
1125 16         26 $tmphash->{__stack} = $this->_copy($config->{__stack});
1126             }
1127              
1128 39         109 $config->{$block} = $this->_parse($tmphash, \@newcontent);
1129             }
1130             }
1131 82         109 undef $blockname;
1132 82         97 undef $block;
1133 82         102 $this->{level} -= 1;
1134 82         107 next;
1135             }
1136             }
1137             else { # inside $block, just push onto new content stack
1138 158         272 push @newcontent, $_;
1139             }
1140             }
1141 134 50       201 if ($block) {
1142             # $block is still defined, which means, that it had
1143             # no matching endblock!
1144 0         0 croak "Config::General: Block \"<$block>\" has no EndBlock statement (level: $this->{level}, chunk $chunk)!\n";
1145             }
1146 134         286 return $config;
1147             }
1148              
1149              
1150             sub _copy {
1151             #
1152             # copy the contents of one hash into another
1153             # to circumvent invalid references
1154             # fixes rt.cpan.org bug #35122
1155 39     39   59 my($this, $source) = @_;
1156 39         46 my %hash = ();
1157 39         42 while (my ($key, $value) = each %{$source}) {
  444         813  
1158 405         608 $hash{$key} = $value;
1159             }
1160 39         81 return \%hash;
1161             }
1162              
1163              
1164             sub _parse_value {
1165             #
1166             # parse the value if value parsing is turned on
1167             # by either -AutoTrue and/or -FlagBits
1168             # otherwise just return the given value unchanged
1169             #
1170 249     249   421 my($this, $config, $option, $value) =@_;
1171              
1172 249         257 my $cont;
1173 249         350 ($cont, $option, $value) = $this->_hook('pre_parse_value', $option, $value);
1174 249 50       394 return $value if(!$cont);
1175              
1176             # avoid "Use of uninitialized value"
1177 249 100       359 if (! defined $value) {
1178             # patch fix rt#54583
1179             # Return an input undefined value without trying transformations
1180 4         10 return $value;
1181             }
1182              
1183 245 50       353 if($this->{NormalizeValue}) {
1184 0         0 $value = $this->{NormalizeValue}($value);
1185             }
1186              
1187 245 100       338 if ($this->{InterPolateVars}) {
1188 73         154 $value = $this->_interpolate($config, $option, $value);
1189             }
1190              
1191             # make true/false values to 1 or 0 (-AutoTrue)
1192 245 100       363 if ($this->{AutoTrue}) {
1193 46 100       236 if ($value =~ /$this->{AutoTrueFlags}->{true}/io) {
    100          
1194 7         9 $value = 1;
1195             }
1196             elsif ($value =~ /$this->{AutoTrueFlags}->{false}/io) {
1197 9         14 $value = 0;
1198             }
1199             }
1200              
1201             # assign predefined flags or undef for every flag | flag ... (-FlagBits)
1202 245 100       362 if ($this->{FlagBits}) {
1203 35 100       55 if (exists $this->{FlagBitsFlags}->{$option}) {
1204 2         9 my %__flags = map { $_ => 1 } split /\s*\|\s*/, $value;
  5         13  
1205 2         4 foreach my $flag (keys %{$this->{FlagBitsFlags}->{$option}}) {
  2         7  
1206 6 100       9 if (exists $__flags{$flag}) {
1207 5         9 $__flags{$flag} = $this->{FlagBitsFlags}->{$option}->{$flag};
1208             }
1209             else {
1210 1         2 $__flags{$flag} = undef;
1211             }
1212             }
1213 2         4 $value = \%__flags;
1214             }
1215             }
1216              
1217 245 100       337 if (!$this->{NoEscape}) {
1218             # are there any escaped characters left? put them out as is
1219 244         358 $value =~ s/\\([\$\\\"#])/$1/g;
1220             }
1221              
1222 245         354 ($cont, $option, $value) = $this->_hook('post_parse_value', $option, $value);
1223              
1224 245         523 return $value;
1225             }
1226              
1227              
1228              
1229             sub _hook {
1230 728     728   1671 my ($this, $hook, @arguments) = @_;
1231 728 50       1117 if(exists $this->{Plug}->{$hook}) {
1232 0         0 my $sub = $this->{Plug}->{$hook};
1233 0         0 my @hooked = &$sub(@arguments);
1234 0         0 return @hooked;
1235             }
1236 728         1695 return (1, @arguments);
1237             }
1238              
1239              
1240              
1241             sub save {
1242             #
1243             # this is the old version of save() whose API interface
1244             # has been changed. I'm very sorry 'bout this.
1245             #
1246             # I'll try to figure out, if it has been called correctly
1247             # and if yes, feed the call to Save(), otherwise croak.
1248             #
1249 0     0 0 0 my($this, $one, @two) = @_;
1250              
1251 0 0 0     0 if ( (@two && $one) && ( (scalar @two) % 2 == 0) ) {
      0        
1252             # @two seems to be a hash
1253 0         0 my %h = @two;
1254 0         0 $this->save_file($one, \%h);
1255             }
1256             else {
1257 0         0 croak q(Config::General: The save() method is deprecated. Use the new save_file() method instead!);
1258             }
1259 0         0 return;
1260             }
1261              
1262              
1263             sub save_file {
1264             #
1265             # save the config back to disk
1266             #
1267 7     7 1 5947 my($this, $file, $config) = @_;
1268 7         11 my $fh;
1269             my $config_string;
1270              
1271 7 50       13 if (!$file) {
1272 0         0 croak "Config::General: Filename is required!";
1273             }
1274             else {
1275 7 50       14 if ($this->{UTF8}) {
1276 0         0 $fh = IO::File->new;
1277 0 0       0 open($fh, ">:utf8", $file)
1278             or croak "Config::General: Could not open $file in UTF8 mode!($!)\n";
1279             }
1280             else {
1281 7 50       31 $fh = IO::File->new( "$file", 'w')
1282             or croak "Config::General: Could not open $file!($!)\n";
1283             }
1284 7 100       831 if (!$config) {
1285 5 50       11 if (exists $this->{config}) {
1286 5         18 $config_string = $this->_store(0, $this->{config});
1287             }
1288             else {
1289 0         0 croak "Config::General: No config hash supplied which could be saved to disk!\n";
1290             }
1291             }
1292             else {
1293 2         11 $config_string = $this->_store(0, $config);
1294             }
1295              
1296 6 50       11 if ($config_string) {
1297 6         8 print {$fh} $config_string;
  6         77  
1298             }
1299             else {
1300             # empty config for whatever reason, I don't care
1301 0         0 print {$fh} q();
  0         0  
1302             }
1303              
1304 6         208 close $fh;
1305             }
1306 6         40 return;
1307             }
1308              
1309              
1310              
1311             sub save_string {
1312             #
1313             # return the saved config as a string
1314             #
1315 1     1 1 92 my($this, $config) = @_;
1316              
1317 1 50 33     7 if (!$config || ref($config) ne 'HASH') {
1318 0 0       0 if (exists $this->{config}) {
1319 0         0 return $this->_store(0, $this->{config});
1320             }
1321             else {
1322 0         0 croak "Config::General: No config hash supplied which could be saved to disk!\n";
1323             }
1324             }
1325             else {
1326 1         4 return $this->_store(0, $config);
1327             }
1328 0         0 return;
1329             }
1330              
1331              
1332              
1333             sub _store {
1334             #
1335             # internal sub for saving a block
1336             #
1337 31     31   47 my($this, $level, $config) = @_;
1338 31         37 local $_;
1339 31         41 my $indent = q( ) x $level;
1340              
1341 31         34 my $config_string = q();
1342              
1343 31 100       94 foreach my $entry ( $this->{SaveSorted} ? sort keys %$config : keys %$config ) {
1344             # fix rt#104548
1345 93 100       226 if ($entry =~ /[<>\n\r]/) {
1346 1         109 croak "Config::General: current key contains invalid characters: $entry!\n";
1347             }
1348              
1349 92 100       182 if (ref($config->{$entry}) eq 'ARRAY') {
    100          
1350 5 100 66     13 if( $this->{ForceArray} && scalar @{$config->{$entry}} == 1 && ! ref($config->{$entry}->[0]) ) {
  1   66     6  
1351             # a single value array forced to stay as array
1352 1         5 $config_string .= $this->_write_scalar($level, $entry, '[' . $config->{$entry}->[0] . ']');
1353             }
1354             else {
1355 4 50       7 foreach my $line ( $this->{SaveSorted} ? sort @{$config->{$entry}} : @{$config->{$entry}} ) {
  0         0  
  4         7  
1356 10 100       34 if (ref($line) eq 'HASH') {
1357 2         10 $config_string .= $this->_write_hash($level, $entry, $line);
1358             }
1359             else {
1360 8         11 $config_string .= $this->_write_scalar($level, $entry, $line);
1361             }
1362             }
1363             }
1364             }
1365             elsif (ref($config->{$entry}) eq 'HASH') {
1366 21         49 $config_string .= $this->_write_hash($level, $entry, $config->{$entry});
1367             }
1368             else {
1369 66         137 $config_string .= $this->_write_scalar($level, $entry, $config->{$entry});
1370             }
1371             }
1372              
1373 30         61 return $config_string;
1374             }
1375              
1376              
1377             sub _write_scalar {
1378             #
1379             # internal sub, which writes a scalar
1380             # it returns it, in fact
1381             #
1382 75     75   113 my($this, $level, $entry, $line) = @_;
1383              
1384 75         103 my $indent = q( ) x $level;
1385              
1386 75         73 my $config_string;
1387              
1388             # patch fix rt#54583
1389 75 50 66     209 if ( ! defined $line ) {
    100          
1390 0         0 $config_string .= $indent . $entry . "\n";
1391             }
1392             elsif ($line =~ /\n/ || $line =~ /\\$/) {
1393             # it is a here doc
1394 3         4 my $delimiter;
1395 3         4 my $tmplimiter = 'EOF';
1396 3         6 while (!$delimiter) {
1397             # create a unique here-doc identifier
1398 3 50       16 if ($line =~ /$tmplimiter/s) {
1399 0         0 $tmplimiter .= '%';
1400             }
1401             else {
1402 3         6 $delimiter = $tmplimiter;
1403             }
1404             }
1405 3         8 my @lines = split /\n/, $line;
1406 3         16 $config_string .= $indent . $entry . $this->{StoreDelimiter} . "<<$delimiter\n";
1407 3         6 foreach (@lines) {
1408 8         15 $config_string .= $indent . $_ . "\n";
1409             }
1410 3         5 $config_string .= $indent . "$delimiter\n";
1411             }
1412             else {
1413             # a simple stupid scalar entry
1414              
1415 72 50       108 if (!$this->{NoEscape}) {
1416             # re-escape contained $ or # or \ chars
1417 72         132 $line =~ s/([#\$\\\"])/\\$1/g;
1418             }
1419              
1420             # bugfix rt.cpan.org#42287
1421 72 100 100     201 if ($line =~ /^\s/ or $line =~ /\s$/) {
1422             # need to quote it
1423 2         5 $line = "\"$line\"";
1424             }
1425 72         138 $config_string .= $indent . $entry . $this->{StoreDelimiter} . $line . "\n";
1426             }
1427              
1428 75         172 return $config_string;
1429             }
1430              
1431             sub _write_hash {
1432             #
1433             # internal sub, which writes a hash (block)
1434             # it returns it, in fact
1435             #
1436 23     23   36 my($this, $level, $entry, $line) = @_;
1437              
1438 23         29 my $indent = q( ) x $level;
1439 23         22 my $config_string;
1440              
1441 23 50       46 if ($entry =~ /\s/) {
1442             # quote the entry if it contains whitespaces
1443 0         0 $entry = q(") . $entry . q(");
1444             }
1445              
1446             # check if the next level key points to a hash and is the only one
1447             # in this case put out a named block
1448             # fixes rt.77667
1449 23         24 my $num = scalar keys %{$line};
  23         36  
1450 23 100       41 if($num == 1) {
1451 17         19 my $key = (keys %{$line})[0];
  17         28  
1452 17 100       39 if(ref($line->{$key}) eq 'HASH') {
1453 11         22 $config_string .= $indent . qq(<$entry $key>\n);
1454 11         20 $config_string .= $this->_store($level + 1, $line->{$key});
1455 11         19 $config_string .= $indent . qq(\n";
1456 11         26 return $config_string;
1457             }
1458             }
1459            
1460 12         22 $config_string .= $indent . q(<) . $entry . ">\n";
1461 12         25 $config_string .= $this->_store($level + 1, $line);
1462 12         17 $config_string .= $indent . q(\n";
1463              
1464 12         42 return $config_string
1465             }
1466              
1467              
1468             sub _hashref {
1469             #
1470             # return a probably tied new empty hash ref
1471             #
1472 154     154   201 my($this) = @_;
1473 154 100       264 if ($this->{Tie}) {
1474 1         2 eval {
1475 1         50 eval qq{require $this->{Tie}};
1476             };
1477 1 50       4 if ($EVAL_ERROR) {
1478 0         0 croak q(Config::General: Could not create a tied hash of type: ) . $this->{Tie} . q(: ) . $EVAL_ERROR;
1479             }
1480 1         2 my %hash;
1481 1         3 tie %hash, $this->{Tie};
1482 1         13 return \%hash;
1483             }
1484             else {
1485 153         290 return {};
1486             }
1487             }
1488              
1489              
1490             #
1491             # Procedural interface
1492             #
1493             sub ParseConfig {
1494             #
1495             # @_ may contain everything which is allowed for new()
1496             #
1497 3     3 1 1375 return (new Config::General(@_))->getall();
1498             }
1499              
1500             sub SaveConfig {
1501             #
1502             # 2 parameters are required, filename and hash ref
1503             #
1504 1     1 1 12 my ($file, $hash) = @_;
1505              
1506 1 50 33     6 if (!$file || !$hash) {
1507 0         0 croak q{Config::General::SaveConfig(): filename and hash argument required.};
1508             }
1509             else {
1510 1 50       3 if (ref($hash) ne 'HASH') {
1511 0         0 croak q(Config::General::SaveConfig() The second parameter must be a reference to a hash!);
1512             }
1513             else {
1514 1         5 (new Config::General(-ConfigHash => $hash))->save_file($file);
1515             }
1516             }
1517 1         7 return;
1518             }
1519              
1520             sub SaveConfigString {
1521             #
1522             # same as SaveConfig, but return the config,
1523             # instead of saving it
1524             #
1525 0     0 1   my ($hash) = @_;
1526              
1527 0 0         if (!$hash) {
1528 0           croak q{Config::General::SaveConfigString(): Hash argument required.};
1529             }
1530             else {
1531 0 0         if (ref($hash) ne 'HASH') {
1532 0           croak q(Config::General::SaveConfigString() The parameter must be a reference to a hash!);
1533             }
1534             else {
1535 0           return (new Config::General(-ConfigHash => $hash))->save_string();
1536             }
1537             }
1538 0           return;
1539             }
1540              
1541              
1542              
1543             # keep this one
1544             1;
1545             __END__