File Coverage

blib/lib/Config/Manager/Conf.pm
Criterion Covered Total %
statement 262 307 85.3
branch 130 166 78.3
condition 76 119 63.8
subroutine 23 25 92.0
pod 0 13 0.0
total 491 630 77.9


line stmt bran cond sub pod time code
1              
2             ###############################################################################
3             ## ##
4             ## Copyright (c) 2003 by Steffen Beyer & Gerhard Albers. ##
5             ## All rights reserved. ##
6             ## ##
7             ## This package is free software; you can redistribute it ##
8             ## and/or modify it under the same terms as Perl itself. ##
9             ## ##
10             ###############################################################################
11              
12             package Config::Manager::Conf;
13              
14             ################################################################################
15             # Im- und Exporte
16             ################################################################################
17              
18 3     3   1709 use strict;
  3         6  
  3         130  
19 3     3   16 use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION %INC %SIG );
  3         7  
  3         20098  
20              
21             require Exporter;
22              
23             @ISA = qw(Exporter);
24              
25             @EXPORT = qw();
26              
27             @EXPORT_OK = qw( whoami );
28              
29             %EXPORT_TAGS = (all => [@EXPORT_OK]);
30              
31             $VERSION = '1.7';
32              
33             ################################################################################
34             # Datenstrukturen
35             ################################################################################
36              
37             # Programminterne Konstanten
38              
39             # Pattern zur Beschreibung von "privaten" Dateien (Sonderbehandlung)
40             my $PRIVATE = "\\bPRIVATE?\\.ini\$";
41              
42             # Besondere Datenquellen
43             my @WHOAMI = qw( USERNAME LOGNAME USER LOGIN );
44             my $USR = '';
45             my $SYS = '';
46             my $EXT = '';
47             # Sections
48             my $ENV = 'ENV';
49             my $SPECIAL = 'SPECIAL';
50             my $DEFAULT = 'DEFAULT';
51             # Keys
52             my $SCOPE = 'SCOPE';
53             my $NEXTCONF = 'NEXTCONF';
54             my $YEAR = 'YEAR';
55             my $MONTH = 'MONTH';
56             my $DAY = 'DAY';
57             my $HOUR = 'HOUR';
58             my $MIN = 'MIN';
59             my $SEC = 'SEC';
60             my $YDAY = 'YDAY';
61             my $WDAY = 'WDAY';
62             my $YY = 'YY';
63             my $CC = 'CC';
64             my $OS = 'OS';
65             my $PERL = 'PERL';
66             my $HOME = 'HOME';
67             my $WHOAMI = 'WHOAMI';
68             # Verarbeitungszustaende
69             my $RAW = 1;
70             my $PENDING = 2;
71             my $CACHED = 3;
72             # Sonstige Konstanten
73             my $NONE = 'NONE';
74              
75             my $SYNTAX = 'Syntax error';
76             my $INFINITE = 'Infinite recursion';
77              
78             my $anchor;
79              
80             my $default = Config::Manager::Conf->new();
81              
82             ################################################################################
83             # Oeffentliche Funktionen
84             ################################################################################
85              
86             sub whoami
87             {
88 4     4 0 6 my($key,$value);
89              
90 4         9 foreach $key (@WHOAMI)
91             {
92 16 50       53 if (defined ($value = $ENV{$key})) { return ($value,$key); }
  0         0  
93             }
94 4         32 return ();
95             }
96              
97             ################################################################################
98             # Oeffentliche Methoden
99             ################################################################################
100              
101             sub add {
102 166     166 0 1125 my $self = shift;
103 166 100       567 ref($self) || ($self = $default);
104 166         170 local($_); # because of foreach
105 166         357 foreach (@_) {
106 282 50 66     5689 next if (!(-r $_) && /$PRIVATE/io);
107 282 100       9152 open(FILE, $_) || return $self->_error("Unable to open file '$_':\n$!");
108 278         19836 my @lines = ;
109 278 50       4451 close(FILE) || return $self->_error("Unable to close file '$_':\n$!");
110 278 100       917 $self->_add($_, \@lines) || return undef;
111             }
112 125         797 return 1;
113             }
114              
115             sub default {
116 3     3 0 26 return $default;
117             }
118              
119             sub error {
120 157     157 0 892 my $self = shift;
121 157 100       320 ref($self) || ($self = $default);
122 157         409 return $$self{''};
123             }
124              
125             sub get {
126 810     810 0 13135 my $self = shift;
127 810 100       2960 ref($self) || ($self = $default);
128 810         951 my $key = pop;
129 810   66     1791 my $section = pop || $DEFAULT;
130 810         2062 my $state = $$self{$section}{$key}{'state'};
131 810         1020 my $value;
132 810         864 local($@); # because of eval{}; and parse()
133 810 100       1448 unless ($state) {
134 75 100 100     282 return $ENV{$key} if $section eq $ENV && defined $ENV{$key};
135 69 0 0     208 if ($section eq $SPECIAL &&
      33        
136             ($key eq $WHOAMI || $key eq $HOME)) {
137 0         0 $$self{$section}{$key}{'source'} = $SYS;
138 0         0 $$self{$section}{$key}{'line'} = 0;
139 0 0       0 unless (($value) = &whoami()) {
140 0         0 return $self->_error( _not_found_($SPECIAL,$WHOAMI) );
141             }
142 0 0       0 return $value if $key eq $WHOAMI;
143             {
144 0         0 local($SIG{'__DIE__'}) = 'DEFAULT';
  0         0  
145 0         0 eval {
146 0         0 ($value) = (getpwnam($value))[7];
147             };
148             }
149 0 0       0 if ($@) {
150 0         0 $value = $@;
151 0         0 $value =~ s!\s+$!!;
152 0 0       0 $value .= " on this platform" if ($value =~ s!\s+at\s+\S.+$!!);
153 0         0 return $self->_error($value);
154             }
155 0 0       0 return $value if defined $value;
156             }
157 69         161 return $self->_error( _not_found_($section,$key) );
158             }
159 735         1725 $value = $$self{$section}{$key}{'value'};
160 735 100       2157 return $value if $state == $CACHED;
161 528 100       937 if ($state == $PENDING) {
162 6         13 my $text = _name_($section,$key) . " = \"$value\"";
163 6         19 my $source = $$self{$section}{$key}{'source'};
164 6         11 my $line = $$self{$section}{$key}{'line'};
165 6         36 return $self->_error($INFINITE, $text, $section, $source, $line);
166             }
167 522         943 $$self{$section}{$key}{'state'} = $PENDING;
168 522 100       1109 if (defined ($value = $self->parse($value, $section))) {
169 480         964 $$self{$section}{$key}{'value'} = $value;
170 480         805 $$self{$section}{$key}{'state'} = $CACHED;
171 480         1931 return $value;
172             }
173             else {
174 42         79 $$self{''} = $@;
175 42         99 $$self{$section}{$key}{'state'} = $RAW;
176 42         118 return undef;
177             }
178             }
179              
180             sub init {
181 3     3 0 162 my $self = shift;
182 3   33     9 my $scope = shift || $DEFAULT;
183 3         7 my $base = __PACKAGE__;
184 3 100       8 ref($self) || ($self = $default);
185 3         11 $self->_init();
186             # Wenn Ankerdatei unbekannt bzw. nicht vorhanden bzw. leer: Neu ermitteln
187 3 50 66     111 unless ($anchor && (-f $anchor) && (-r $anchor) && (-s $anchor)) {
      66        
      33        
188             # Anker ist die Datei "Conf.ini", die im selben Verzeichnis wie die
189             # Moduldatei "Conf.pm" selbst liegt; dazu wird %INC herangezogen.
190 2         10 $base =~ s!::!/!g;
191 2         15 $anchor = $INC{"$base.pm"};
192 2         11 $anchor =~ s!\.pm$!.ini!;
193 2 50 33     189 unless ($anchor && (-f $anchor) && (-r $anchor) && (-s $anchor)) {
      33        
      33        
194 0         0 $anchor = undef;
195 0         0 return $self->_error("Can't locate '$base.ini' in %INC");
196             }
197             }
198 3   33     11 return $self->set($SYS, $SPECIAL, $SCOPE, $scope) && $self->add($anchor);
199             }
200              
201             sub new {
202 165     165 0 3608 my $this = shift;
203 165   50     848 my $class = ref($this) || $this || __PACKAGE__;
204 165         283 my $self = {};
205 165         763 bless $self, $class;
206 165         418 return $self->_init();
207             }
208              
209             sub parse {
210 23966     23966 0 32387 my($self,$text,$eval) = @_;
211 23966         21439 my($left,$right,$first,$var);
212 23966         29526 $@ = '';
213 23966 100       92313 return $text unless $text =~ /\$/;
214 8667         12825 $left = $`;
215 8667         12204 $right = $';
216 8667 100       17358 if (length($right) == 0) {
217 1         2 $@ = "illegal '\$' at end of string";
218 1         4 return undef;
219             }
220 8666         11297 $first = substr($right,0,1);
221 8666 100       13912 if ($first eq '$') {
222 464 100       980 $left .= '$' unless $eval;
223 464 50       1213 return undef unless defined($right = $self->parse(substr($right,1),$eval));
224 464         1781 return $left . '$' . $right;
225             }
226             else {
227 8202 100       15813 return undef unless (($var,$right) = $self->_parse_var($first,$right,$eval));
228 8152 100       19477 return undef unless defined($right = $self->parse($right,$eval));
229 8148         27481 return $left . $var . $right;
230             }
231             }
232              
233             sub scope {
234 282     282 0 424 my $self = shift;
235 282 100       645 ref($self) || ($self = $default);
236 282         615 return $self->get($SPECIAL, $SCOPE);
237             }
238              
239             sub set {
240 2316     2316 0 4851 my $self = shift;
241 2316 100       4517 ref($self) || ($self = $default);
242 2316         2709 my $value = pop;
243 2316         2310 my $key = pop;
244 2316   66     4472 my $section = pop || $DEFAULT;
245 2316   66     4036 my $source = pop || $USR;
246 2316 100 100     9189 return $self->_error( _read_only_($SPECIAL,$key) )
      100        
      66        
247             if ($section eq $SPECIAL && $source ne $SYS &&
248             ($key eq $OS || $key eq $PERL || $key eq $SCOPE));
249 2314         4470 return $self->_set($source, 0, $section, $key, $value, 1);
250             }
251              
252             sub get_all {
253 1     1 0 7 my $self = shift;
254 1         3 my $list = [];
255 1 50       4 ref($self) || ($self = $default);
256 1         2 foreach my $sec (sort keys(%{$self})) {
  1         8  
257             next unless
258 2 100 66     15 (($sec =~ /^[a-zA-Z][a-zA-Z0-9_-]*$/) &&
259             (substr($sec,-1) ne '-'));
260 1         1 foreach my $key (sort keys(%{${$self}{$sec}})) {
  1         1  
  1         8  
261 13         25 my $val = $self->get($sec,$key);
262 13         15 my $ok = 1;
263 13 50       22 unless (defined $val) {
264 0         0 $val = $self->error();
265 0         0 $val =~ s!\s+$!!;
266 0         0 $ok = 0;
267             }
268             push(
269 13         12 @{$list},
  13         22  
270             [
271             $ok,
272             _name_($sec,$key),
273             $val,
274             $$self{$sec}{$key}{'source'},
275             $$self{$sec}{$key}{'line'}
276             ]
277             );
278             }
279             }
280 1         14 foreach my $key (sort keys(%ENV)) {
281 22         33 push(
282 22         20 @{$list},
283             [
284             1,
285             _name_($ENV,$key),
286             $ENV{$key},
287             $EXT,
288             0
289             ]
290             );
291             }
292 1         4 return $list;
293             }
294              
295             sub get_section {
296 0     0 0 0 my $self = shift;
297 0   0     0 my $sec = shift || $DEFAULT;
298 0         0 my $hash = {};
299 0 0       0 ref($self) || ($self = $default);
300 0         0 foreach my $key (keys %{${$self}{$sec}}) {
  0         0  
  0         0  
301 0         0 my $val = $self->get($sec,$key);
302 0 0       0 if (defined $val)
303             {
304 0         0 ${$hash}{$key} = $val;
  0         0  
305             }
306             # else
307             # {
308             # $val = $self->error();
309             # $val =~ s!\s+$!!;
310             # ${$hash}{$key} = $val;
311             # }
312             }
313 0         0 return $hash;
314             }
315              
316             sub get_files {
317 0     0 0 0 return [ @{shift->{''}} ];
  0         0  
318             }
319              
320             ################################################################################
321             # Private Methoden
322             ################################################################################
323              
324             sub _init {
325 168     168   236 my $self = shift;
326             # Alle frueheren Eintraege loeschen:
327 168         260 %{$self} = ();
  168         555  
328             # Liste der eingelesenen Dateien anlegen:
329 168         483 $$self{''} = [];
330             # Datumsangaben fuer SPECIAL-Section aus localtime() holen:
331 168         7178 my @localtime = localtime();
332             # Jahresangabe bezieht sich auf das Basisjahr 1900:
333 168         481 $localtime[5] += 1900;
334             # Monat ist im Bereich 0-11, daher eins addieren:
335 168         190 $localtime[4]++;
336             # Der erste Januar ist in localtime() der nullte Tag, daher eins addieren:
337 168         201 $localtime[7]++;
338             # Der Wochentag Sonntag ist in localtime() mit Null kodiert:
339 168 50       394 $localtime[6] = 7 unless ($localtime[6]);
340             # Tag und Monat zweistellig fuer eindeutige Zeitstempel (2000123 kann der
341             # 3. Dezember oder der 23. Januar sein); Tag des Jahres dreistellig:
342 168         525 $self->set($SYS, $SPECIAL, $YEAR, $localtime[5]);
343 168         1009 $self->set($SYS, $SPECIAL, $MONTH, sprintf('%02d',$localtime[4]));
344 168         685 $self->set($SYS, $SPECIAL, $DAY, sprintf('%02d',$localtime[3]));
345 168         687 $self->set($SYS, $SPECIAL, $HOUR, sprintf('%02d',$localtime[2]));
346 168         630 $self->set($SYS, $SPECIAL, $MIN, sprintf('%02d',$localtime[1]));
347 168         672 $self->set($SYS, $SPECIAL, $SEC, sprintf('%02d',$localtime[0]));
348 168         716 $self->set($SYS, $SPECIAL, $YDAY, sprintf('%03d',$localtime[7]));
349 168         450 $self->set($SYS, $SPECIAL, $WDAY, $localtime[6] );
350 168         749 $self->set($SYS, $SPECIAL, $YY, sprintf('%02d',$localtime[5]%100));
351 168         778 $self->set($SYS, $SPECIAL, $CC, int($localtime[5]/100));
352 168         494 $self->set($SYS, $SPECIAL, $OS, $^O);
353 168         470 $self->set($SYS, $SPECIAL, $PERL, $^X);
354 168         379 $self->set($SYS, $SPECIAL, $SCOPE, $NONE);
355 168         588 return $self;
356             }
357              
358             sub _add {
359 278     278   465 my($self,$file,$list) = @_;
360 278         350 my $line = 0;
361 278         342 my $section = $DEFAULT;
362 278         594 my $scope = $self->scope();
363 278         412 my $next = '';
364 278         376 my @next = ();
365 278         314 local($_); # because of foreach
366 278         255 local($@); # because of parse()
367 278         262 push( @{$$self{''}}, $file );
  278         669  
368 278         467 foreach (@$list) {
369 24410         23177 $line++;
370             # Leerzeilen und Kommentarzeilen ignorieren
371 24410 100 100     125845 /^\s*(\S)/ && $1 ne '#' || next;
372             # Leerzeichen und Zeilenumbruch vom Zeilenende entfernen
373 14171         36045 s/\s+$//;
374             # Neuer Abschnitt?
375 14171 100 100     42652 if (/^\s*\[\s*([a-zA-Z][a-zA-Z0-9_-]*)\s*\]$/ && substr($1,-1) ne '-') {
376 1636         2192 $section = $1;
377 1636         2230 next;
378             }
379             # Text in Schluessel und Wert zerlegen
380 12535 100 100     93969 unless (/^\s*\$?([a-zA-Z][a-zA-Z0-9_-]*)\s*=\s*(.*?\S.*?)\s*$/ && substr($1,-1) ne '-') {
381 22         78 return $self->_error($SYNTAX, $_, $section, $file, $line);
382             }
383 12513         19101 my $key = $1;
384 12513         17207 my $value = $2; # ist ggf. in doppelte Anfuehrungszeichen verpackt
385 12513         16470 $value =~ s/^\s*"(.*)"\s*$/$1/;
386 12513 100       25448 return $self->_error( _read_only_($SPECIAL,$key) )
387             if $section eq $SPECIAL;
388 12512 100 100     27661 if (($key eq $NEXTCONF) && ($section eq $scope)) {
389 10         26 $next = $value;
390             }
391 12512 100       24477 $self->_set($file, $line, $section, $key, $value) || return undef;
392             }
393 241 100       5246 return 1 if $next eq '';
394 8 50       17 return $self->add($next)
395             if (defined ($next = $self->parse($next, $scope)));
396 0         0 $$self{''} = $@;
397 0         0 return undef;
398             }
399              
400             sub _error {
401 119     119   218 my($self, $text, $description, $section, $source, $line) = @_;
402 119         173 my $location = '';
403 119 50 66     631 if (defined $section || defined $source || defined $line) {
      66        
404 37         68 $location = ' in';
405 37 50       88 $location .= " file '$source'" if $source;
406 37 50       74 $location .= " line #$line" if $line;
407 37 50       82 $location .= " [$section]" if $section;
408             }
409 119 100       245 $description = $description ? ": $description" : '';
410 119         291 $$self{''} = $text . $location . $description;
411 119         535 return undef;
412             }
413              
414             sub _set {
415 14826     14826   26465 my($self, $source, $line, $section, $key, $value, $override) = @_;
416 14826         15402 local($@); # because of parse()
417 14826 100 33     50006 return $self->_error( _read_only_($section,$key) )
      66        
      66        
418             if ($section eq $ENV || ($section eq $SPECIAL &&
419             ($key eq $HOME || $key eq $WHOAMI)));
420 14824         38718 my $src = $$self{$section}{$key}{'source'};
421 14824 100 100     34740 if (defined $src && $src eq $source && $src ne $SYS && $src ne $USR) {
      100        
      66        
422 4         13 my $error = "Double entry in file '$src' for configuration constant " . _name_($section,$key);
423 4 50 33     25 if ($line && $$self{$section}{$key}{'line'}) {
424 4         16 $error .= " in line #$$self{$section}{$key}{'line'} and #$line";
425             }
426 4         11 return $self->_error($error);
427             }
428 14820 100       25977 unless (defined $self->parse($value)) {
429 9         55 return $self->_error($SYNTAX, $@, $section, $source, $line);
430             }
431 14811 100 100     54185 if ($override || not $src) {
432 14578         37653 $$self{$section}{$key}{'source'} = $source;
433 14578         29214 $$self{$section}{$key}{'line'} = $line;
434 14578         44431 $$self{$section}{$key}{'value'} = $value;
435 14578         26753 $$self{$section}{$key}{'state'} = $RAW;
436             }
437 14811         45798 return 1;
438             }
439              
440             ################################################################################
441             # Private Funktionen
442             ################################################################################
443              
444             sub _name_ {
445 155     155   211 my $key = pop;
446 155   33     343 my $sec = pop || $DEFAULT;
447 155         848 return "\$[$sec]{$key}";
448             }
449              
450             sub _not_found_ {
451 105     105   226 return "Configuration constant " . _name_(@_) . " not found";
452             }
453              
454             sub _read_only_ {
455 5     5   12 return "Configuration constant " . _name_(@_) . " is read-only";
456             }
457              
458             ############################################################
459             # Private Hilfsmethoden fuer parse()
460             ############################################################
461              
462             sub _parse_id {
463             # Aufrufer muss sicherstellen, dass $text mit einem Buchstaben [A-Za-z] beginnt!
464 12406     12406   15799 my($self,$text) = @_;
465 12406         25623 $text =~ /^([a-zA-Z][a-zA-Z0-9_-]*)/;
466 12406 100       88469 return ($1,$') unless substr($1,-1) eq '-';
467 1         5 $@ = "illegal terminating '-' in identifier '$1'";
468 1         8 return ();
469             }
470              
471             sub _parse_sub {
472             # Aufrufer muss sicherstellen, dass $rest auf dem Anfang eines moeglichen '$' oder [A-Za-z] steht
473 8408     8408   15481 my($self,$rest,$eval) = @_;
474 8408         7872 my($first,$variable);
475 8408 100       20670 if (length($rest) == 0) {
476 1         1 $@ = "expecting identifier or variable, unexpected end of string";
477 1         7 return ();
478             }
479 8407         11128 $first = substr($rest,0,1);
480 8407 100       23153 if ($first eq '$') {
    50          
481 1276         1746 $rest = substr($rest,1);
482 1276 50       2342 if (length($rest) == 0) {
483 0         0 $@ = "found '$', expecting variable, unexpected end of string";
484 0         0 return ();
485             }
486 1276         1442 $first = substr($rest,0,1);
487 1276         2671 return (($variable,$rest) = $self->_parse_var($first,$rest,$eval));
488             }
489             elsif ($first =~ /^[A-Za-z]$/) {
490 7131         12683 return (($variable,$rest) = $self->_parse_id($rest,$eval));
491             }
492             else {
493 0         0 $@ = "expecting identifier or variable, found '$first', expected '$' or [A-Za-z]";
494 0         0 return ();
495             }
496             }
497              
498             sub _parse_var {
499             # Aufrufer muss sicherstellen, dass vor $first ein '$' war und dass $first erster Char von $rest ist
500 9478     9478   15378 my($self,$first,$rest,$eval) = @_;
501 9478         8750 my($section,$variable,$value);
502 9478         9976 $section = '';
503 9478 100       22459 if ($first eq '[') {
504 4210 50       9998 return () unless (($section,$rest) = $self->_parse_sub(substr($rest,1),$eval));
505 4210 50       12051 if (length($rest) == 0) {
506 0         0 $@ = "missing ']' after section name '$section', unexpected end of string";
507 0         0 return ();
508             }
509 4210         5820 $first = substr($rest,0,1);
510 4210 100       7815 if ($first ne ']') {
511 1         3 $@ = "missing ']' after section name '$section', found '$first' instead";
512 1         5 return ();
513             }
514 4209         5489 $rest = substr($rest,1);
515 4209 100       9742 if (length($rest) == 0) {
516 1         3 $@ = "missing key name after section name '$section', unexpected end of string";
517 1         7 return ();
518             }
519 4208         5784 $first = substr($rest,0,1);
520             }
521 9476 100       22457 if ($first eq '{') {
    100          
522 4198 100       11189 return () unless (($variable,$rest) = $self->_parse_sub(substr($rest,1),$eval));
523 4197 100       12585 if (length($rest) == 0) {
524 1         4 $@ = "missing '}' after variable name '$variable', unexpected end of string";
525 1         7 return ();
526             }
527 4196         5766 $first = substr($rest,0,1);
528 4196 50       7950 if ($first ne '}') {
529 0         0 $@ = "missing '}' after variable name '$variable', found '$first' instead";
530 0         0 return ();
531             }
532 4196         4834 $rest = substr($rest,1);
533 4196 100       10735 if ($eval) {
534 81 100 66     330 return ($value,$rest) if defined ($value = $self->get($section || $eval, $variable));
535 16         56 $@ = $self->error();
536 16 100 66     81 return () if $section || $@ ne _not_found_($eval, $variable);
537 10         31 $@ = '';
538 10 100       22 return ($value,$rest) if defined ($value = $self->get($variable));
539 6         13 $@ = $self->error();
540 6         32 return ();
541             }
542             else {
543 4115 100       6223 if ($section eq '') { return( "[$section]{$variable}", $rest ); }
  1482         7341  
544 2633         12942 else { return( "{$variable}", $rest ); }
545             }
546             }
547             elsif ($first =~ /^[A-Za-z]$/) {
548 5275 100       13847 return () unless (($variable,$rest) = $self->_parse_id($rest,$eval));
549 5274 100       10763 if ($eval) {
550 140 100 66     653 return ($value,$rest) if defined ($value = $self->get($section || $eval, $variable));
551 34         112 $@ = $self->error();
552 34 100 100     145 return () if $section || $@ ne _not_found_($eval, $variable);
553 10         92 $@ = '';
554 10 100       25 return ($value,$rest) if defined ($value = $self->get($variable));
555 6         14 $@ = $self->error();
556 6         37 return ();
557             }
558             else {
559 5134 100       8261 if ($section eq '') { return( "[$section]$variable", $rest ); }
  3648         17269  
560 1486         5942 else { return( $variable, $rest ); }
561             }
562             }
563             else {
564 3 100       9 if ($section eq '') { $@ = "found '\$' followed by '$first', expecting '{' or [A-Za-z]"; }
  2         6  
565 1         5 else { $@ = "found '\$[$section]' followed by '$first', expecting '{' or [A-Za-z]"; }
566 3         16 return ();
567             }
568             }
569              
570             1;
571              
572             __END__