← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 18:42:36 2015
Reported on Fri Jul 31 18:48:14 2015

Filename/var/www/foswikidev/core/lib/Foswiki/Configure/LoadSpec.pm
StatementsExecuted 32 statements in 2.92ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
1111.89ms2.03msFoswiki::Configure::LoadSpec::::BEGIN@62Foswiki::Configure::LoadSpec::BEGIN@62
111989µs2.59msFoswiki::Configure::LoadSpec::::BEGIN@61Foswiki::Configure::LoadSpec::BEGIN@61
111198µs282µsFoswiki::Configure::LoadSpec::::BEGIN@66Foswiki::Configure::LoadSpec::BEGIN@66
11113µs26µsFoswiki::Configure::LoadSpec::::BEGIN@56Foswiki::Configure::LoadSpec::BEGIN@56
11113µs17µsFoswiki::Configure::LoadSpec::::BEGIN@57Foswiki::Configure::LoadSpec::BEGIN@57
11112µs24µsFoswiki::Configure::LoadSpec::::BEGIN@354Foswiki::Configure::LoadSpec::BEGIN@354
11110µs14µsFoswiki::Configure::LoadSpec::::BEGIN@356Foswiki::Configure::LoadSpec::BEGIN@356
11110µs40µsFoswiki::Configure::LoadSpec::::BEGIN@59Foswiki::Configure::LoadSpec::BEGIN@59
1118µs39µsFoswiki::Configure::LoadSpec::::BEGIN@72Foswiki::Configure::LoadSpec::BEGIN@72
1115µs5µsFoswiki::Configure::LoadSpec::::BEGIN@63Foswiki::Configure::LoadSpec::BEGIN@63
1114µs4µsFoswiki::Configure::LoadSpec::::BEGIN@67Foswiki::Configure::LoadSpec::BEGIN@67
1114µs4µsFoswiki::Configure::LoadSpec::::BEGIN@64Foswiki::Configure::LoadSpec::BEGIN@64
1113µs3µsFoswiki::Configure::LoadSpec::::BEGIN@65Foswiki::Configure::LoadSpec::BEGIN@65
0000s0sFoswiki::Configure::LoadSpec::::_debugItemFoswiki::Configure::LoadSpec::_debugItem
0000s0sFoswiki::Configure::LoadSpec::::_extractSectionsFoswiki::Configure::LoadSpec::_extractSections
0000s0sFoswiki::Configure::LoadSpec::::_getValueObjectFoswiki::Configure::LoadSpec::_getValueObject
0000s0sFoswiki::Configure::LoadSpec::::_loadSpecsFromFoswiki::Configure::LoadSpec::_loadSpecsFrom
0000s0sFoswiki::Configure::LoadSpec::::addCfgValuesToSpecFoswiki::Configure::LoadSpec::addCfgValuesToSpec
0000s0sFoswiki::Configure::LoadSpec::::addSpecDefaultsToCfgFoswiki::Configure::LoadSpec::addSpecDefaultsToCfg
0000s0sFoswiki::Configure::LoadSpec::::parseFoswiki::Configure::LoadSpec::parse
0000s0sFoswiki::Configure::LoadSpec::::protectKeyFoswiki::Configure::LoadSpec::protectKey
0000s0sFoswiki::Configure::LoadSpec::::protectKeysFoswiki::Configure::LoadSpec::protectKeys
0000s0sFoswiki::Configure::LoadSpec::::readSpecFoswiki::Configure::LoadSpec::readSpec
0000s0sSectionMarker::::getValueObject SectionMarker::getValueObject
0000s0sSectionMarker::::new SectionMarker::new
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2package Foswiki::Configure::LoadSpec;
3
4=begin TML
5
6---+ package Foswiki::Configure::LoadSpec
7
8This is a parser for configuration declaration files, such as
9Foswiki.spec, and the Config.spec files in extensions.
10
11The supported syntax in declaration files is as follows:
12<verbatim>
13cfg ::= ( setting | section | extension )* ;
14setting ::= BOL typespec EOL comment* BOL def ;
15typespec ::= "**" typeid options "**" ;
16def ::= "$" ["Foswiki::"] "cfg" keys "=" value ";" ;
17keys ::= ( "{" id "}" )+ ;
18value is any perl value not including ";"
19comment ::= BOL "#" string EOL ;
20section ::= BOL "#--+" string ( "--" options )? EOL comment* ;
21extension ::= BOL " *" id "*"
22EOL ::= end of line
23BOL ::= beginning of line
24typeid ::= id ;
25id ::= a \w+ word (legal Perl bareword)
26</verbatim>
27
28A *section* is simply a divider used to create blocks. It can
29 have varying depth depending on the number of + signs and may have
30 options after -- e.g. #---+ Section -- TABS EXPERT
31
32A *setting* is the sugar required for the setting of a single
33 configuration value.
34
35An *extension* is a pluggable UI extension that supports some extra UI
36 functionality, such as the menu of languages or the menu of plugins.
37
38Each *setting* has a *typespec* and a *def*.
39
40The typespec consists of a type id and some options.
41
42A *def* is a specification of a field in the $Foswiki::cfg hash,
43together with a perl value for that hash. Each field can have an
44associated *Checker* which is loaded from the Foswiki::Configure::Checkers
45hierarchy. Checkers are responsible for specific checks on the value of
46that variable. For example, the checker for $Foswiki::cfg{Banana}{Republic}
47will be expected to be found in
48Foswiki::Configure::Checkers::Banana::Republic.
49Checkers are subclasses of Foswiki::Configure::Checker. See that class for
50more details.
51
52An *extension* is a placeholder for a Foswiki::Configure::Pluggable.
53
54=cut
55
56226µs240µs
# spent 26µs (13+13) within Foswiki::Configure::LoadSpec::BEGIN@56 which was called: # once (13µs+13µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 56
use strict;
# spent 26µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@56 # spent 13µs making 1 call to strict::import
57230µs221µs
# spent 17µs (13+4) within Foswiki::Configure::LoadSpec::BEGIN@57 which was called: # once (13µs+4µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 57
use warnings;
# spent 17µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@57 # spent 4µs making 1 call to warnings::import
58
59227µs270µs
# spent 40µs (10+30) within Foswiki::Configure::LoadSpec::BEGIN@59 which was called: # once (10µs+30µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 59
use Assert;
# spent 40µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@59 # spent 30µs making 1 call to Exporter::import
60
61298µs12.59ms
# spent 2.59ms (989µs+1.61) within Foswiki::Configure::LoadSpec::BEGIN@61 which was called: # once (989µs+1.61ms) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 61
use Foswiki::Configure::Section ();
# spent 2.59ms making 1 call to Foswiki::Configure::LoadSpec::BEGIN@61
622114µs12.03ms
# spent 2.03ms (1.89+143µs) within Foswiki::Configure::LoadSpec::BEGIN@62 which was called: # once (1.89ms+143µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 62
use Foswiki::Configure::Value ();
# spent 2.03ms making 1 call to Foswiki::Configure::LoadSpec::BEGIN@62
63220µs15µs
# spent 5µs within Foswiki::Configure::LoadSpec::BEGIN@63 which was called: # once (5µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 63
use Foswiki::Configure::Item ();
# spent 5µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@63
64218µs14µs
# spent 4µs within Foswiki::Configure::LoadSpec::BEGIN@64 which was called: # once (4µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 64
use Foswiki::Configure::Load ();
# spent 4µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@64
65218µs13µs
# spent 3µs within Foswiki::Configure::LoadSpec::BEGIN@65 which was called: # once (3µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 65
use Foswiki::Configure::FileUtil ();
# spent 3µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@65
662109µs1282µs
# spent 282µs (198+85) within Foswiki::Configure::LoadSpec::BEGIN@66 which was called: # once (198µs+85µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 66
use Foswiki::Configure::Pluggable ();
# spent 282µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@66
67242µs14µs
# spent 4µs within Foswiki::Configure::LoadSpec::BEGIN@67 which was called: # once (4µs+0s) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 67
use Foswiki::Configure::Reporter ();
# spent 4µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@67
68
691400nsour $TRUE = 1; # Required for checking default value syntax
701100nsour $FALSE = 0;
71
7221.22ms270µs
# spent 39µs (8+31) within Foswiki::Configure::LoadSpec::BEGIN@72 which was called: # once (8µs+31µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 72
use constant TRACE => 0;
# spent 39µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@72 # spent 31µs making 1 call to constant::import
73
74=begin TML
75
76---++ Global $RAW_VALS
77Set true to suppress parsing of attribute values (FEEDBACK and
78CHECK strings) and simply store them as strings. This is
79useful for performance, when these items are not required.
80Default behaviour is to parse the strings.
81
82=cut
83
841100nsour $RAW_VALS = 0;
85
86sub _debugItem {
87 my $item = shift;
88 return ( $item->{typename} || 'Section' ) . ' '
89 . ( $item->{keys} || $item->{headline} || '???' );
90}
91
92=begin TML
93
94---++ StaticMethod readSpec($root, $reporter)
95
96Load the configuration declarations. The core set is defined in
97Foswiki.spec, which must be found on the @INC path and is always loaded
98first. Then find all settings for extensions in their .spec files.
99
100This *only* reads type specifications, it *does not* read values. For that,
101use Foswiki::Configure::Load::readConfig.
102
103 * =$root= - Foswiki::Configure::Root of the model
104
105=cut
106
107sub readSpec {
108 my ( $root, $reporter ) = @_;
109
110 my $file = Foswiki::Configure::FileUtil::findFileOnPath('Foswiki.spec');
111 if ($file) {
112 parse( $file, $root, $reporter );
113 }
114
115 my %read;
116 foreach my $dir (@INC) {
117 foreach my $subdir (
118 'Foswiki/Plugins', 'Foswiki/Contrib',
119 'TWiki/Plugins', 'TWiki/Contrib'
120 )
121 {
122
123 _loadSpecsFrom( "$dir/$subdir", $root, \%read, $reporter );
124 }
125 }
126}
127
128sub _loadSpecsFrom {
129 my ( $dir, $root, $read, $reporter ) = @_;
130
131 return unless opendir( D, $dir );
132
133 # note we ignore specs from any extension where the name starts
134 # with "Empty" e.g. EmptyPlugin, EmptyContrib
135 foreach my $extension ( sort grep { !/^\./ && !/^Empty/ } readdir D ) {
136
137 next if $read->{$extension};
138
139 $extension =~ m/(.*)/;
140 $extension = $1; # untaint
141 my $file = "$dir/$extension/Config.spec";
142 next unless -e $file;
143 parse( $file, $root, $reporter );
144 $read->{$extension} = $file;
145 }
146 closedir(D);
147}
148
149{
150
151 # Inner class that represents section headings temporarily during the
152 # parse. They are expanded to section blocks at the end.
1531400ns package SectionMarker;
15418µs @SectionMarker::ISA = ('Foswiki::Configure::Item');
155
156 sub new {
157 my ( $class, $depth, $head ) = @_;
158 my $this = bless( {}, $class );
159 $this->{Depth} = $depth + 1;
160 $this->{Headline} = $head;
161 return $this;
162 }
163
164 sub getValueObject { return; }
165}
166
167# Process the config array and add section objects
168sub _extractSections {
169 my ( $settings, $root ) = @_;
170
171 my $section = $root;
172 my $depth = 0;
173
174 foreach my $item (@$settings) {
175 if ( $item->isa('SectionMarker') ) {
176 my $opts = '';
177 if ( $item->{Headline} =~ s/^(.*?)\s*--\s*(.*?)\s*$/$1/ ) {
178 $opts = $2;
179 }
180 my $ns =
181 $root->getSectionObject( $item->{Headline}, $item->{Depth} );
182 if ($ns) {
183 $depth = $item->{Depth};
184 }
185 else {
186 while ( $depth > $item->{Depth} - 1 ) {
187 $section = $section->{_parent};
188 $depth--;
189 }
190 while ( $depth < $item->{Depth} - 1 ) {
191 my $ns = new Foswiki::Configure::Section();
192 $section->addChild($ns);
193 $section = $ns;
194 $depth++;
195 }
196 $ns = new Foswiki::Configure::Section(
197 headline => $item->{Headline},
198 opts => $opts
199 );
200 $ns->{desc} = $item->{desc};
201 $section->addChild($ns);
202 $depth++;
203 }
204 $section = $ns;
205 }
206 elsif ( $item->isa('Foswiki::Configure::Value') ) {
207
208 # Skip it if we already have a settings object for these
209 # keys (first loaded always takes precedence, irrespective
210 # of which section it is in)
211 my $vo = $root->getValueObject( $item->{keys} );
212 next if ($vo);
213 $section->addChild($item);
214 }
215 else {
216 $section->addChild($item);
217 }
218 }
219}
220
221# See if we have already build a value object for these keys
222sub _getValueObject {
223 my ( $keys, $settings ) = @_;
224 foreach my $item (@$settings) {
225 my $i = $item->getValueObject($keys);
226 return $i if $i;
227 }
228 return;
229}
230
231=begin TML
232
233---++ StaticMethod parse($file, $root, $reporter)
234
235Parse the config declaration file and add it to a root node for the
236configuration it describes
237
238=cut
239
240sub parse {
241 my ( $file, $root, $reporter ) = @_;
242 my $fh;
243
244 unless ( open( $fh, '<', $file ) ) {
245 $reporter->ERROR("$file open failed: $!");
246 return '';
247 }
248
249 local $/ = "\n";
250 my $open = undef; # current setting or section
251 my $isEnhancing = 0; # Is the current $open an existing item being enhanced?
252 my @settings;
253 my $sectionNum = 0;
254
255 $reporter->NOTE("Loading specs from $file") if TRACE;
256
257 while ( my $l = <$fh> ) {
258 chomp $l;
259
260 my $context = "$file: $.";
261
262 # Continuation lines
263
264 while ( $l =~ s/\\$// ) {
265 my $cont = <$fh>;
266 last unless defined $cont;
267 chomp $cont;
268 $cont =~ s/^#// if ( $l =~ m/^#/ );
269 $cont =~ s/^\s+/ /;
270 if ( $cont =~ m/^#/ ) {
271 $l .= '\\';
272 }
273 else {
274 $l .= $cont;
275 }
276 }
277
278 last if ( $l =~ m/^(1;|__[A-Z]+__)/ );
279 next if ( $l =~ m/^\s*$/ || $l =~ m/^\s*#!/ );
280
281 if ( $l =~ m/^#\s*\*\*\s*([A-Z]+)\s*(.*?)\s*\*\*\s*$/ ) {
282
283 # **STRING 30 EXPERT**
284 if ( $open && !$isEnhancing ) {
285 $reporter->NOTE(
286 "\tClosed " . _debugItem($open) . ' at ' . __LINE__ )
287 if TRACE;
288 push( @settings, $open );
289 $open = undef;
290 }
291 if ( $1 eq 'ENHANCE' ) {
292
293 # Enhance an existing value
294 $open = $root->getValueObject($2);
295 $reporter->ERROR("$context: No such value $2")
296 unless $open;
297 $isEnhancing = $open ? 1 : 0;
298 $reporter->NOTE("\tEnhancing $open->{keys}")
299 if TRACE && $open;
300 }
301 else {
302 my $type = $1;
303 my $opts = $2;
304 eval {
305 $open = Foswiki::Configure::Value->new(
306 $type,
307 opts => $opts,
308 defined_at => [ $file, $. ]
309 );
310 $reporter->NOTE("\tOpened $open->{typename}") if TRACE;
311 };
312
313 if ($@) {
314 $reporter->ERROR("$context: $@");
315 $open = undef;
316 }
317 $isEnhancing = 0;
318 }
319 }
320
321 elsif (
322 $l =~ m/^(#)?\s*\$(?:(?:Fosw|TW)iki::)?cfg([^=\s]*)\s*=\s*(.*?)$/ )
323 {
324
325 # $Foswiki::cfg{Rice}{Brown} =
326
327 my $optional = $1;
328 my $keys = $2;
329 my $value = $3;
330
331 if ( $keys eq '{WebMasterName}' ) {
332 ASSERT($open) if DEBUG;
333 }
334 unless ( $keys =~ m/^$Foswiki::Configure::Load::ITEMREGEX$/ ) {
335 $reporter->ERROR("$context: Invalid item specifier $keys");
336 $open = undef;
337 next;
338 }
339
340 # Restore initial \n for continued lines
341 $value .= "\n" unless $value =~ m/\s*;\s*$/;
342
343 # Read the value verbatim, retaining internal \s
344 while ( $value !~ s/\s*;\s*$//s ) {
345 my $cont = <$fh>;
346 last unless defined $cont;
347 $value .= $cont;
348 }
349
350 # check it's a valid perl expression, ignoring uninitialised
351 # variable warnings inside strings.
352 $value =~ m/^\s*(.*?)[\s;]*$/s; # trim and untaint
353 $value = $1;
354233µs235µs
# spent 24µs (12+12) within Foswiki::Configure::LoadSpec::BEGIN@354 which was called: # once (12µs+12µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 354
no warnings;
# spent 24µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@354 # spent 12µs making 1 call to warnings::unimport
355 eval($value);
35621.16ms217µs
# spent 14µs (10+4) within Foswiki::Configure::LoadSpec::BEGIN@356 which was called: # once (10µs+4µs) by Foswiki::Plugins::ConfigurePlugin::BEGIN@38 at line 356
use warnings;
# spent 14µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@356 # spent 4µs making 1 call to warnings::import
357 $reporter->ERROR( "$context: Cannot eval value '$value': "
358 . Foswiki::Configure::Reporter::stripStacktrace($@) )
359 if $@;
360
361 if ( $open && $open->isa('SectionMarker') ) {
362 unless ($isEnhancing) {
363 $reporter->NOTE(
364 "\tClosed " . _debugItem($open) . ' at ' . __LINE__ )
365 if TRACE;
366 push( @settings, $open );
367 $open = undef;
368 }
369 }
370
371 if ( !$open ) {
372 next if $root->getValueObject($keys);
373
374 # A pluggable may have already added an entry for these keys
375 next if ( _getValueObject( $keys, \@settings ) );
376
377 # This is an untyped value.
378 $open = Foswiki::Configure::Value->new('UNKNOWN');
379 $isEnhancing = 0;
380 }
381 $open->{defined_at} = [ $file, $. ];
382
383 # Record the value *string*, internal formatting et al.
384 # This is the best way to retain perl formatting while
385 # being sensitive to changes.
386 $open->{default} = $1;
387
388 # Configure treats all regular expressions as simple quoted string,
389 # Convert from qr/ / notation to a simple quoted string
390 if ( $open->{typename} eq 'REGEX'
391 && $open->{default} =~ m/^qr(.)(.*)\1$/ )
392 {
393
394 # Convert a qr// into a quoted string
395
396 # Strip off useless furniture (?^: ... )
397 while ( $open->{default} =~ s/^\(\?\^:(.*)\)$/$1/ ) {
398 }
399
400 # Convert quoting for a single-quoted string. All we
401 # need to do is protect single quote
402 $open->{default} =~ s/'/\\\\'/g;
403 $open->{default} = "'" . $open->{default} . "'";
404 }
405
406 $open->{keys} = $keys;
407 unless ($isEnhancing) {
408 $reporter->NOTE(
409 "\tClosed " . _debugItem($open) . ' at ' . __LINE__ )
410 if TRACE;
411 push( @settings, $open );
412 }
413 $open = undef;
414 $isEnhancing = 0;
415 }
416
417 elsif ( $l =~ m/^#\s*\*([A-Z]+)\*/ ) {
418
419 # *FINDEXTENSIONS* pluggable
420 my $name = $1;
421 my $subset = \@settings;
422
423 if ($isEnhancing) {
424 $reporter->ERROR(
425 "$context: Cannot ENHANCE a non-section with a Pluggable")
426 if ( $open
427 && !$open->isa('Foswiki::Configure::Section') );
428 $subset = \@{ $open->{children} };
429 $isEnhancing = $open;
430 }
431 elsif ($open) {
432 if ( !$open->isa('Foswiki::Configure::Section')
433 && !$open->isa('SectionMarker') )
434 {
435 my $otype = $open->{typename} || $open;
436 $reporter->ERROR("$context: Incomplete $otype declaration");
437 }
438 elsif ( !$isEnhancing ) {
439 push( @settings, $open );
440 $reporter->NOTE(
441 "\tClosed " . _debugItem($open) . ' at ' . __LINE__ )
442 if TRACE;
443 }
444 $open = undef;
445 }
446
447 eval {
448 Foswiki::Configure::Pluggable::load( $name, $subset, $file,
449 $. );
450 };
451 if ($@) {
452 $reporter->WARN("Can't load pluggable $name: $@");
453 }
454 elsif ($isEnhancing) {
455
456 # Have to shoehorn in parent links
457 foreach my $kid (@$subset) {
458 $kid->{_parent} = $isEnhancing
459 unless $kid->{_parent};
460 }
461 }
462
463 $isEnhancing = 0;
464 }
465
466 elsif ( $l =~ m/^#\s*---\+(\+*) *(.*?)$/ ) {
467
468 # ---++ Section
469 $sectionNum++;
470 if ( $open && !$isEnhancing ) {
471
472 # We have an open item. If it's a value, we don't want to create
473 # it since that will confuse the UI. Report such errors.
474 if ( $open->isa('Foswiki::Configure::Value') ) {
475 my $otype = $open->{typename};
476 $reporter->ERROR("$context: Incomplete $otype declaration");
477 }
478 elsif ( !$isEnhancing ) {
479 push( @settings, $open );
480 $reporter->NOTE(
481 "\tClosed " . _debugItem($open) . ' at ' . __LINE__ )
482 if TRACE;
483 }
484 }
485 $open = new SectionMarker( length($1), $2 );
486 $isEnhancing = 0;
487 }
488
489 elsif ( $l =~ m/^#\s?(.*)$/ ) {
490
491 # Bog standard comment
492 $open->append( 'desc', $1 ) if $open;
493 }
494 }
495 close($fh);
496 if ( $open && !$isEnhancing ) {
497 if ( $open->isa('Foswiki::Configure::Value') ) {
498 my $otype = $open->{typename};
499 $reporter->ERROR("$file:$.: Incomplete $otype declaration");
500 }
501 else {
502 push( @settings, $open ) unless $isEnhancing;
503 $reporter->NOTE(
504 "\tClosed " . _debugItem($open) . ' at ' . __LINE__ )
505 if TRACE;
506 }
507 }
508 _extractSections( \@settings, $root );
509
510 # Promote the EXPERT setting up to those containers where
511 # all children have it
512 $root->promoteSetting('EXPERT');
513}
514
515=begin TML
516
517---++ StaticMethod protectKeys($keystring) -> $keystring
518
519Process a key string {Like}{This} and make sure that each key is
520safe for use in an eval.
521
522=cut
523
524sub protectKeys {
525 my $k = shift;
526 $k =~ s/^\{(.*)\}$/$1/;
527 return '{'
528 . join(
529 '}{', map { protectKey($_) }
530 split( /\}\{/, $k )
531 ) . '}';
532}
533
534=begin TML
535
536---++ StaticMethod protectKey($keystring) -> $keystring
537
538Process a key string (a hash index) and make sure that it is
539safe for use as a perl hash index.
540
541=cut
542
543sub protectKey {
544 my $k = shift;
545 return $k if $k =~ m/^[a-z_][a-z0-9_]*$/i;
546
547 # Remove existing quotes, if there
548 $k =~ s/^(["'])(.*)\1$/$2/i;
549
550 # Use ' to suppress interpolation (just in case)
551 $k =~ s/'/\\'/g; # escape '
552 $k = "'$k'";
553 if (DEBUG) {
554 eval($k);
555 ASSERT( !$@, $k );
556 }
557 return $k;
558}
559
560=begin TML
561
562---++ StaticMethod addSpecDefaultsToCfg($spec, \%cfg)
563
564 * =$spec= - ref to a Foswiki::Configure::Item
565 * =\%cfg= ref to a cfg hash e.g. Foswiki::cfg
566
567For each key in the $spec missing from the %cfg passed, add the
568default (unexpanded) from the spec to the %cfg, if it exists.
569
570=cut
571
572sub addSpecDefaultsToCfg {
573 my ( $spec, $cfg ) = @_;
574
575 if ( $spec->{children} ) {
576 foreach my $child ( @{ $spec->{children} } ) {
577 addSpecDefaultsToCfg( $child, $cfg );
578 }
579 }
580 else {
581 if ( exists( $spec->{default} )
582 && eval("!exists(\$cfg->$spec->{keys})") )
583 {
584 # {default} stores a value string. Convert it to the
585 # value suitable for storing in cfg
586 print STDERR "Defaulting $spec->{keys}\n" if TRACE;
587 my $value = eval( $spec->{default} );
588 eval("\$cfg->$spec->{keys}=$spec->{default}");
589 }
590 }
591}
592
593=begin TML
594
595---++ StaticMethod addCfgValuesToSpec(\%cfg, $spec)
596
597 * =\%cfg= ref to a cfg hash e.g. Foswiki::cfg
598 * =$spec= - ref to a Foswiki::Configure::Item
599
600For each key in the spec add the current value from the %cfg
601as current_value. If the key is
602not set in the %cfg, then set it to the default.
603Note that the %cfg should contain *unexpanded* values.
604
605=cut
606
607sub addCfgValuesToSpec {
608 my ( $cfg, $spec ) = @_;
609 if ( $spec->{children} ) {
610 foreach my $child ( @{ $spec->{children} } ) {
611 addCfgValuesToSpec( $cfg, $child );
612 }
613 }
614 else {
615 if ( eval("exists(\$cfg->$spec->{keys})") ) {
616
617 # Encode the value as something that can be handled by
618 # UIs
619 my $value = eval("\$cfg->$spec->{keys}");
620 ASSERT( !$@ ) if DEBUG;
621 $spec->{current_value} = $spec->encodeValue($value);
622 }
623
624 # Don't do this; it's not the case that the default value
625 # will end up in LocalSite.cfg
626 #elsif (exists($spec->{default})) {
627 # eval("\$spec->{current_value}=eval(\$spec->{default})");
628 #}
629 }
630}
631
63214µs1;
633__END__