Filename | /var/www/foswikidev/core/lib/Foswiki/Configure/LoadSpec.pm |
Statements | Executed 32 statements in 2.92ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 1.89ms | 2.03ms | BEGIN@62 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 989µs | 2.59ms | BEGIN@61 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 198µs | 282µs | BEGIN@66 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 13µs | 26µs | BEGIN@56 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 13µs | 17µs | BEGIN@57 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 12µs | 24µs | BEGIN@354 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 10µs | 14µs | BEGIN@356 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 10µs | 40µs | BEGIN@59 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 8µs | 39µs | BEGIN@72 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 5µs | 5µs | BEGIN@63 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 4µs | 4µs | BEGIN@67 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 4µs | 4µs | BEGIN@64 | Foswiki::Configure::LoadSpec::
1 | 1 | 1 | 3µs | 3µs | BEGIN@65 | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | _debugItem | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | _extractSections | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | _getValueObject | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | _loadSpecsFrom | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | addCfgValuesToSpec | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | addSpecDefaultsToCfg | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | parse | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | protectKey | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | protectKeys | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | readSpec | Foswiki::Configure::LoadSpec::
0 | 0 | 0 | 0s | 0s | getValueObject | SectionMarker::
0 | 0 | 0 | 0s | 0s | new | SectionMarker::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | package Foswiki::Configure::LoadSpec; | ||||
3 | |||||
4 | =begin TML | ||||
5 | |||||
6 | ---+ package Foswiki::Configure::LoadSpec | ||||
7 | |||||
8 | This is a parser for configuration declaration files, such as | ||||
9 | Foswiki.spec, and the Config.spec files in extensions. | ||||
10 | |||||
11 | The supported syntax in declaration files is as follows: | ||||
12 | <verbatim> | ||||
13 | cfg ::= ( setting | section | extension )* ; | ||||
14 | setting ::= BOL typespec EOL comment* BOL def ; | ||||
15 | typespec ::= "**" typeid options "**" ; | ||||
16 | def ::= "$" ["Foswiki::"] "cfg" keys "=" value ";" ; | ||||
17 | keys ::= ( "{" id "}" )+ ; | ||||
18 | value is any perl value not including ";" | ||||
19 | comment ::= BOL "#" string EOL ; | ||||
20 | section ::= BOL "#--+" string ( "--" options )? EOL comment* ; | ||||
21 | extension ::= BOL " *" id "*" | ||||
22 | EOL ::= end of line | ||||
23 | BOL ::= beginning of line | ||||
24 | typeid ::= id ; | ||||
25 | id ::= a \w+ word (legal Perl bareword) | ||||
26 | </verbatim> | ||||
27 | |||||
28 | A *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 | |||||
32 | A *setting* is the sugar required for the setting of a single | ||||
33 | configuration value. | ||||
34 | |||||
35 | An *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 | |||||
38 | Each *setting* has a *typespec* and a *def*. | ||||
39 | |||||
40 | The typespec consists of a type id and some options. | ||||
41 | |||||
42 | A *def* is a specification of a field in the $Foswiki::cfg hash, | ||||
43 | together with a perl value for that hash. Each field can have an | ||||
44 | associated *Checker* which is loaded from the Foswiki::Configure::Checkers | ||||
45 | hierarchy. Checkers are responsible for specific checks on the value of | ||||
46 | that variable. For example, the checker for $Foswiki::cfg{Banana}{Republic} | ||||
47 | will be expected to be found in | ||||
48 | Foswiki::Configure::Checkers::Banana::Republic. | ||||
49 | Checkers are subclasses of Foswiki::Configure::Checker. See that class for | ||||
50 | more details. | ||||
51 | |||||
52 | An *extension* is a placeholder for a Foswiki::Configure::Pluggable. | ||||
53 | |||||
54 | =cut | ||||
55 | |||||
56 | 2 | 26µs | 2 | 40µ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 # spent 26µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@56
# spent 13µs making 1 call to strict::import |
57 | 2 | 30µs | 2 | 21µ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 # spent 17µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@57
# spent 4µs making 1 call to warnings::import |
58 | |||||
59 | 2 | 27µs | 2 | 70µ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 # spent 40µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@59
# spent 30µs making 1 call to Exporter::import |
60 | |||||
61 | 2 | 98µs | 1 | 2.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 # spent 2.59ms making 1 call to Foswiki::Configure::LoadSpec::BEGIN@61 |
62 | 2 | 114µs | 1 | 2.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 # spent 2.03ms making 1 call to Foswiki::Configure::LoadSpec::BEGIN@62 |
63 | 2 | 20µs | 1 | 5µ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 # spent 5µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@63 |
64 | 2 | 18µs | 1 | 4µ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 # spent 4µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@64 |
65 | 2 | 18µs | 1 | 3µ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 # spent 3µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@65 |
66 | 2 | 109µs | 1 | 282µ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 # spent 282µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@66 |
67 | 2 | 42µs | 1 | 4µ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 # spent 4µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@67 |
68 | |||||
69 | 1 | 400ns | our $TRUE = 1; # Required for checking default value syntax | ||
70 | 1 | 100ns | our $FALSE = 0; | ||
71 | |||||
72 | 2 | 1.22ms | 2 | 70µ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 # 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 | ||||
77 | Set true to suppress parsing of attribute values (FEEDBACK and | ||||
78 | CHECK strings) and simply store them as strings. This is | ||||
79 | useful for performance, when these items are not required. | ||||
80 | Default behaviour is to parse the strings. | ||||
81 | |||||
82 | =cut | ||||
83 | |||||
84 | 1 | 100ns | our $RAW_VALS = 0; | ||
85 | |||||
86 | sub _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 | |||||
96 | Load the configuration declarations. The core set is defined in | ||||
97 | Foswiki.spec, which must be found on the @INC path and is always loaded | ||||
98 | first. Then find all settings for extensions in their .spec files. | ||||
99 | |||||
100 | This *only* reads type specifications, it *does not* read values. For that, | ||||
101 | use Foswiki::Configure::Load::readConfig. | ||||
102 | |||||
103 | * =$root= - Foswiki::Configure::Root of the model | ||||
104 | |||||
105 | =cut | ||||
106 | |||||
107 | sub 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 | |||||
128 | sub _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. | ||||
153 | 1 | 400ns | package SectionMarker; | ||
154 | 1 | 8µ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 | ||||
168 | sub _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 | ||||
222 | sub _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 | |||||
235 | Parse the config declaration file and add it to a root node for the | ||||
236 | configuration it describes | ||||
237 | |||||
238 | =cut | ||||
239 | |||||
240 | sub 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; | ||||
354 | 2 | 33µs | 2 | 35µ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 # spent 24µs making 1 call to Foswiki::Configure::LoadSpec::BEGIN@354
# spent 12µs making 1 call to warnings::unimport |
355 | eval($value); | ||||
356 | 2 | 1.16ms | 2 | 17µ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 # 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 | |||||
519 | Process a key string {Like}{This} and make sure that each key is | ||||
520 | safe for use in an eval. | ||||
521 | |||||
522 | =cut | ||||
523 | |||||
524 | sub 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 | |||||
538 | Process a key string (a hash index) and make sure that it is | ||||
539 | safe for use as a perl hash index. | ||||
540 | |||||
541 | =cut | ||||
542 | |||||
543 | sub 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 | |||||
567 | For each key in the $spec missing from the %cfg passed, add the | ||||
568 | default (unexpanded) from the spec to the %cfg, if it exists. | ||||
569 | |||||
570 | =cut | ||||
571 | |||||
572 | sub 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 | |||||
600 | For each key in the spec add the current value from the %cfg | ||||
601 | as current_value. If the key is | ||||
602 | not set in the %cfg, then set it to the default. | ||||
603 | Note that the %cfg should contain *unexpanded* values. | ||||
604 | |||||
605 | =cut | ||||
606 | |||||
607 | sub 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 | |||||
632 | 1 | 4µs | 1; | ||
633 | __END__ |