File Coverage

blib/lib/Config/General/Hierarchical.pm
Criterion Covered Total %
statement 290 294 98.6
branch 187 188 99.4
condition 54 56 96.4
subroutine 28 29 96.5
pod 6 15 40.0
total 565 582 97.0


line stmt bran cond sub pod time code
1             #
2             # Config::General::Hierarchical.pm - Hierarchical Generic Config Module
3             #
4             # Purpose: Permits to organize configuration values
5             # in a hierarchical structure of files
6             #
7             # Copyright (c) 2007-2009 Daniele Ricci .
8             # All Rights Reserved. Std. disclaimer applies.
9             # Artificial License, same as perl itself.
10              
11             package Config::General::Hierarchical;
12              
13             $Config::General::Hierarchical::VERSION = 0.07;
14              
15 5     5   4321 use strict;
  5         12  
  5         193  
16 5     5   28 use warnings;
  5         9  
  5         158  
17              
18 5     5   30 use Carp;
  5         9  
  5         437  
19 5     5   4448 use Clone::PP qw( clone );
  5         19698  
  5         36  
20 5     5   6675 use Config::General;
  5         325791  
  5         434  
21 5     5   3509 use Config::General::Hierarchical::ExcludeWeaken;
  5         22  
  5         189  
22 5     5   31 use Cwd qw( abs_path );
  5         10  
  5         289  
23 5     5   32 use Scalar::Util qw( weaken );
  5         13  
  5         589  
24              
25 5     5   34 use base 'Class::Accessor::Fast';
  5         46  
  5         6071  
26              
27             my @properties = qw( constraint name opt value );
28             my %properties = map( ( $_ => 1 ), @properties );
29              
30             __PACKAGE__->mk_accessors( @properties, qw( cache ) );
31              
32             my %Config_General_Proxy = (
33             '-AutoLaunder' => 1,
34             '-CComments' => 1,
35             '-LowerCaseNames' => 1,
36             '-SplitDelimiter' => 1,
37             '-SplitPolicy' => 1,
38             );
39              
40             sub new {
41 89     89 1 248401 my ( $ref, %args ) = @_;
42              
43 89         159 my $file;
44             my %general;
45 0         0 my %props;
46 0         0 my %options;
47              
48 89         266 foreach my $key ( keys %args ) {
49 230 100       961 if ( $key eq 'file' ) {
    100          
    100          
    100          
50 42         166 $file = $args{$key};
51             }
52             elsif ( $properties{$key} ) {
53 164         367 $props{$key} = $args{$key};
54             }
55             elsif ( $Config::General::Hierarchical::Options::options{$key} ) {
56 19         146 $options{$key} = $args{$key};
57             }
58             elsif ( $Config_General_Proxy{$key} ) {
59 2         7 $general{$key} = $args{$key};
60             }
61             }
62              
63 89 100 100     788 my $class = ref $ref || $ref or croak __PACKAGE__ . ": wrong new call";
64 88         804 my $self = $class->SUPER::new(
65             {
66             cache => {},
67             %props
68             }
69             );
70              
71 88 100       1405 unless (%props) {
72 45         740 $self->opt(
73             Config::General::Hierarchical::Options->new(
74             {
75             files => [],
76             general => \%general,
77             inherits => 'inherits',
78             root => $self,
79             struct => { '0' => {} },
80             undefined => 'undefined',
81             wild => '*',
82             %options
83             }
84             )
85             );
86              
87 45 50       1273 weaken( $self->opt->{root} )
88             unless $Config::General::Hierarchical::ExcludeWeaken::exclude;
89              
90 45 100       856 $self->read($file) if $file;
91              
92 36 100       129 $self->check if $args{check};
93             }
94              
95 78         483 return $self;
96             }
97              
98             sub check {
99 5     5 1 7 my ($self) = @_;
100              
101 5         6 foreach my $key ( keys %{ $self->value } ) {
  5         14  
102 9         37 my $v = $self->get($key);
103              
104 8 100       28 $v->check if eval { $v->isa(__PACKAGE__); };
  8         62  
105             }
106              
107 4         9 return $self;
108             }
109              
110             sub import {
111 12     12   3900 my ( $class, @pars ) = @_;
112 12         75 my $syntax = $class->syntax;
113              
114 12 100       85 die "$class: syntax method musts return an HASH reference\n"
115             if ref $syntax ne 'HASH';
116              
117 11         74 $class->check_syntax( $syntax, [] );
118             }
119              
120             sub check_syntax {
121 32     32 0 59 my ( $class, $syntax, $parents ) = @_;
122              
123 32         2928 foreach my $key ( keys %$syntax ) {
124 79         199 my $ref = ref $syntax->{$key};
125 79         102 my $syn = $syntax->{$key};
126              
127 79         102 push @$parents, $key;
128              
129 79 100       143 if ($ref) {
130 22 100       130 die "$class: wrong use of $ref reference as syntax for variable '"
131             . join( '->', @$parents ) . "'\n"
132             if $ref ne 'HASH';
133              
134 21         79 $class->check_syntax( $syn, $parents );
135             }
136             else {
137 57 100 100     309 die "$class: wrong '$syn' syntax for variable '"
138             . join( '->', @$parents ) . "'\n"
139             if defined $syn && $syn !~ /^[amuABDEINST]*$/;
140              
141 56 100 100     251 die
      100        
      100        
142             "$class: wrong use of 'm' flag for not string nor array variable '"
143             . join( '->', @$parents ) . "'\n"
144             if defined $syn
145             && $syn =~ /m/
146             && syntax_check_get_type($syn) ne 'S'
147             && $syn !~ /a/;
148             }
149              
150 75         230 pop @$parents;
151             }
152             }
153              
154             sub get {
155 251     251 1 2971 my ( $self, $name, @names ) = @_;
156              
157 251 100       706 my $vname = $self->name ? $self->name . '->' . $name : $name;
158              
159 251 100       2383 if ( exists $self->cache->{$name} ) {
160 52         347 my $value = $self->cache->{$name};
161              
162 52 100       482 return $value unless scalar @names;
163 9 100       15 return $value->get(@names) if eval { $value->isa(__PACKAGE__) };
  9         71  
164              
165 3         551 croak __PACKAGE__
166             . ": can't get subkey '$names[0]' value for not node variable '$vname'";
167             }
168              
169 199 100       1412 my $syntax =
170             exists $self->constraint->{$name}
171             ? $self->constraint->{$name}
172             : $self->constraint->{ $self->wild };
173 199   100     2311 my $value = $self->value->{$name} || $self->value->{ $self->wild };
174              
175 199 100 100     1772 if ( !defined $value || ref $value->value ne 'HASH' ) {
176 159 100       1398 croak __PACKAGE__
177             . ": can't get subkey '$names[0]' value for not node variable '$vname'"
178             if scalar @names;
179              
180 158         506 return $self->cache->{$name} =
181             $self->syntax_check( $vname, $value, $syntax );
182             }
183              
184 40   100     533 $self->cache->{$name} = $value = $self->syntax_check(
185             $vname,
186             $self->new(
187             constraint => $syntax || {},
188             name => $vname,
189             opt => $self->opt,
190             value => $value->value
191             ),
192             $syntax
193             );
194              
195 36 100       462 return $value unless scalar @names;
196              
197 3         17 return $value->get(@names);
198             }
199              
200             our $AUTOLOAD;
201              
202             sub AUTOLOAD {
203 738     738   87739 my ( $self, @args ) = @_;
204 738         1142 my $name = $AUTOLOAD;
205              
206 738         4318 $name =~ s/\w+:://g;
207              
208 738 100       5976 return $self->get( $1, @args ) if $name =~ /^_(\w+)$/;
209              
210 606 100       2785 return $self->opt->$name
211             if $Config::General::Hierarchical::Options::options{$name};
212              
213 1         3 my $ref = ref $self;
214              
215 1         185 croak "Can't locate object method \"$name\" via package \"$ref\"";
216             }
217              
218             # simply avoid AUTOLOAD is called when an object is destroied
219 0     0   0 sub DESTROY { }
220              
221             sub getk {
222 2     2 1 10756 my ($self) = @_;
223              
224 2 100       24 croak __PACKAGE__ . ": can't get keys before reading any file"
225             unless $self->value;
226              
227 1         12 return keys %{ $self->value };
  1         4  
228             }
229              
230             sub read {
231 42     42 1 89 my ( $self, $name ) = @_;
232              
233 42         280 $self->constraint( $self->syntax );
234 42         374 $self->value( $self->read_( $name, [] ) );
235 33         274 $self->expand_wild_keys( $self->value );
236 33         222 $self->name('');
237              
238 33         211 return $self;
239             }
240              
241             sub read_ {
242 65     65 0 113 my ( $self, $name, $children ) = @_;
243              
244 65         102 my $tmp = eval { abs_path($name); };
  65         5658  
245 65   66     419 my $error = $@ || !$tmp;
246 65 100       179 $name = $tmp if $tmp;
247 65         293 my $files = $self->opt->files;
248 65         811 my $in_file =
249             $name
250             . join( '', reverse( map( "\ninherited by: $files->[$_]", @$children ) ) )
251             . "\n ";
252 65         115 my $nfile = scalar @$files;
253              
254 65 100       406 croak __PACKAGE__ . ": no such directory: $in_file" if $error;
255 64 100       1919 croak __PACKAGE__ . ": no such file: $in_file" unless -e $name;
256 63 100       1118 croak __PACKAGE__ . ": recursive hierarchy\nin file: $in_file"
257             if grep /^$name$/, map $files->[$_], @$children;
258 62         154 push @$files, $name;
259              
260 62         90 my $cfg;
261 62         117 eval {
262 62         192 $cfg = Config::General->new(
263             '-AllowMultiOptions' => 1,
264             '-AutoTrue' => 0,
265             '-CComments' => 0,
266             '-ConfigFile' => $name,
267             '-ExtendedAccess' => 0,
268             '-InterPolateEnv' => 0,
269             '-InterPolateVars' => 0,
270             '-MergeDuplicateBlocks' => 1,
271             '-MergeDuplicateOptions' => 0,
272             '-SlashIsDirectory' => 0,
273             '-UseApacheInclude' => 0,
274 62         126 ( %{ $self->opt->general } )
275             );
276             };
277              
278 62 100       103608 if ($@) {
279 3         15 my @list = split /\n/, $@;
280 3         7 pop @list;
281 3         10 my $msg = join "\n", @list;
282              
283 3         5211 croak __PACKAGE__ . ": $msg\nin file: $in_file";
284             }
285              
286 59 100       185 if ( scalar @$children ) {
287 21         84 my $str = $self->opt->struct;
288              
289 21         270 foreach (@$children) {
290 23         73 $str = $str->{$_};
291             }
292              
293 21         68 $str->{$nfile} = {};
294             }
295              
296 59         273 my $hash = { $cfg->getall };
297 59         1003 my $syntax = $self->inherits;
298 59         6700 my $inherits = $hash->{$syntax};
299              
300 59         101 delete $hash->{$syntax};
301 59         184 my ( $undefined, $value ) = $self->convert_hash( $hash, $nfile, $in_file );
302              
303 57 100       154 if ($inherits) {
304 19         33 my $parents;
305              
306 19 100       70 if ( !ref $inherits ) {
    100          
307 13         32 $parents = [$inherits];
308             }
309             elsif ( ref $inherits eq 'ARRAY' ) {
310 5         9 $parents = $inherits;
311             }
312             else {
313 1         232 croak __PACKAGE__
314             . ": wrong use of inherits ('$syntax') directive\nin file: $in_file";
315             }
316              
317 18         48 push @$children, @$files - 1;
318 18         53 foreach my $parent ( reverse @$parents ) {
319 23 100       77 if ( $parent =~ /^\// ) {
320 1         2 $name = $parent;
321             }
322             else {
323 22         125 my @list = split /\//, $name;
324 22         46 pop @list;
325 22         114 $name = join( '/', @list ) . '/' . $parent;
326             }
327              
328 23         132 $hash = $self->read_( $name, $children );
329              
330 20         93 $self->merge_values( $value, $hash, $self->constraint );
331             }
332 15         150 pop @$children;
333             }
334              
335 53         187 $self->undefine( $value, $undefined, $nfile );
336              
337 53         1141 return $value;
338             }
339              
340             sub convert_hash {
341 129     129 0 255 my ( $self, $hash, $file, $in_file ) = @_;
342              
343 129         556 my $syntax = $self->inherits;
344 129         1100 my %undefined;
345              
346 129 100       548 croak __PACKAGE__
347             . ": inherits ('$syntax') directive cannot be used as node name\nin file: $in_file"
348             if exists $hash->{$syntax};
349              
350 128         523 $syntax = $self->undefined;
351 128         1304 my $undefined = $hash->{$syntax};
352 128         191 delete $hash->{$syntax};
353              
354 128         387 foreach my $key ( keys %$hash ) {
355 262 100       2693 if ( ref $hash->{$key} eq 'HASH' ) {
356 70         6899 ( $undefined{$key}, my $tmp ) =
357             $self->convert_hash( $hash->{$key}, $file, $in_file );
358             }
359 261 100       645 $hash->{$key} = '' unless defined $hash->{$key};
360 261         1506 $hash->{$key} = Config::General::Hierarchical::Value->new(
361             { value => $hash->{$key}, file => $file } );
362             }
363              
364 127 100       1449 if ($undefined) {
365 13 100       63 if ( !ref $undefined ) {
    100          
366 6         145 $undefined{$undefined} = undef;
367             }
368             elsif ( ref $undefined eq 'ARRAY' ) {
369 6         32 $undefined{$_} = undef foreach @$undefined;
370             }
371             else {
372 1         162 croak __PACKAGE__
373             . ": wrong use of undefined ('$syntax') directive\nin file: $in_file";
374             }
375             }
376              
377 126         435 return ( \%undefined, $hash );
378             }
379              
380             sub merge_values {
381 74     74 0 544 my ( $self, $value, $hash, $constraint ) = @_;
382 74         98 my ( $key, $val );
383              
384 74         330 foreach $key ( keys %$value ) {
385 360         1483 $val = $value->{$key};
386              
387 360 100       998 if ( ref $val eq 'Config::General::Hierarchical::Value' ) {
    100          
388 132 100       351 my $other = exists $hash->{$key} ? $hash->{$key}->value : undef;
389 132   100     672 my $syn = (
390             exists $constraint->{$key}
391             ? $constraint->{$key}
392             : $constraint->{ $self->wild }
393             )
394             || {};
395              
396 132 100 100     928 if ( ref $val->value eq 'HASH' ) {
    100 100        
397 48 100       392 if ( ref $other eq 'HASH' ) {
398 20         66 $self->merge_values( $val->value, $other, $syn );
399             }
400             }
401             elsif ( !ref $syn && $syn =~ /m/ && defined $other ) {
402 13 100       153 if ( $syn =~ /a/ ) {
403 3 100       120 $other = [$other] unless ref $other;
404              
405 3 100       13 if ( ref $val->value ) {
406 1         8 unshift @{ $val->value }, @$other;
  1         4  
407             }
408             else {
409 2         13 $val->value( [ @$other, $val->value ] );
410             }
411             }
412             else {
413 10         26 $val->value( $other . $val->value );
414             }
415             }
416             }
417             elsif ( ref $val eq 'HASH' ) {
418 80 100 100     314 if ( exists $hash->{$key} && ref $hash->{$key} eq 'HASH' ) {
419 8         23 $self->merge_values( $val, $hash->{$key} );
420             }
421             }
422             }
423              
424 74         466 foreach $key ( keys %$hash ) {
425 269 100       756 $value->{$key} = $hash->{$key} unless exists $value->{$key};
426             }
427              
428 74         299 return $value;
429             }
430              
431             sub expand_wild_keys {
432 80     80 0 442 my ( $self, $value ) = @_;
433              
434 80         531 my $wild = $self->wild;
435 80         904 my $wv = $value->{$wild};
436              
437 80 100 100     320 if ( $wv && ref $wv->value eq 'HASH' ) {
438 2         17 $wv = $wv->value;
439              
440 2         9 foreach my $key ( keys %$value ) {
441 5         46 my $v = $value->{$key}->value;
442              
443 5 100       26 next if $key eq $wild;
444 3 100       9 next if ref $v ne 'HASH';
445              
446 2         6 foreach my $wk ( keys %$wv ) {
447 4 100       265 $v->{$wk} = clone( $wv->{$wk} ) unless exists $v->{$wk};
448             }
449             }
450             }
451              
452 80         473 foreach my $key ( keys %$value ) {
453 216         760 my $v = $value->{$key}->value;
454              
455 216 100       1525 next if $key eq $wild;
456 213 100       967 next if ref $v ne 'HASH';
457              
458 47         4380 $self->expand_wild_keys($v);
459             }
460             }
461              
462             sub undefine {
463 122     122 0 756 my ( $self, $value, $undefined, $file ) = @_;
464              
465 122         459 foreach my $key ( keys %$value ) {
466 316 100       1026 next unless exists $undefined->{$key};
467              
468 87 100       501 if ( defined $undefined->{$key} ) {
469 69         295 $self->undefine( $value->{$key}->value, $undefined->{$key}, $file );
470             }
471             else {
472 18         95 $value->{$key} = Config::General::Hierarchical::Value->new(
473             { value => undef, file => $file } );
474             }
475             }
476             }
477              
478             sub syntax {
479 53     53 1 612 return {};
480             }
481              
482             my %types = (
483             A => { d => 'datetime', e => '^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$' },
484             B => { d => 'boolean value', e => '^(0|1|on|off|yes|no|true|false)$' },
485             D => { d => 'date', e => '^\d\d\d\d-\d\d-\d\d$' },
486             E => { d => 'e-mail address', e => '^[\w\d\-_\.]+@[\w\d\-_\.]+\.\w+$' },
487             I => { d => 'integer value', e => '^-?\d+$' },
488             N => { d => 'number', e => '^-?\d*\.?\d+$' },
489             S => { d => 'string' },
490             T => { d => 'time', e => '^\d\d:\d\d:\d\d$' },
491             );
492              
493             sub syntax_check {
494 198     198 0 372 my ( $self, $vname, $value, $syntax ) = @_;
495 198         295 my $can_be_undefined;
496 198         315 my $file = 0;
497              
498 198 100 100     1484 if ( defined $value
499             && ref $value eq 'Config::General::Hierarchical::Value' )
500             {
501 146         564 $file = $value->file;
502 146         799 $value = $value->value;
503             }
504 198         1075 $file = "\nin file: " . $self->opt->files->[$file];
505              
506 198 100       1707 if ( defined $syntax ) {
    100          
507 102 100       368 $can_be_undefined =
508             ref $syntax ? exists $syntax->{ $self->undefined } : $syntax =~ /u/;
509             }
510             elsif ( defined $value ) {
511 91 100       462 return ref $value ? $value : $self->convert_value( $value, $vname )
    100          
512             if ref $value ne 'ARRAY';
513              
514 2         314 croak __PACKAGE__
515             . ": variable '$vname' is an array: should be a string or a node$file";
516             }
517              
518 107 100       452 unless ( defined $value ) {
519 25 100       61 if ($can_be_undefined) {
520 12 100       40 return undef if ref $syntax;
521 10 100       36 return [] if $syntax =~ /a/;
522 9 100       28 return 0 if $syntax =~ /B/;
523 8         43 return undef;
524             }
525              
526 13         3306 croak __PACKAGE__ . ": request for undefined variable '$vname'$file";
527             }
528              
529 82 100       254 if ( ref $syntax ) {
530 19 100       38 return $value if eval { $value->isa(__PACKAGE__) };
  19         171  
531              
532 2         394 croak __PACKAGE__ . ": variable '$vname' should be a node$file";
533             }
534              
535 63         145 my $type = syntax_check_get_type($syntax);
536              
537 63 100       214 if ( $syntax !~ /a/ ) {
538 40 100       508 croak __PACKAGE__
539             . ": variable '$vname' is an array but should be a $types{$type}->{d}$file"
540             if ref $value eq 'ARRAY'; #"}"
541 38 100       556 croak __PACKAGE__
542             . ": variable '$vname' is a node but should be a $types{$type}->{d}$file"
543             if ref $value; #"}"
544              
545 35         100 $value = $self->convert_value( $value, $vname );
546              
547 35 100       333 return $value if $type eq 'S';
548              
549 27         86 $value =~ s/^\s+//;
550 27         53 $value =~ s/\s+$//;
551              
552 27 100       6175 croak __PACKAGE__
553             . ": value '$value' for variable '$vname' is not a prooper $types{$type}->{d}$file"
554             unless $value =~ /$types{$type}->{e}/i; #}"}"
555              
556 12 100       107 return $value if $type ne 'B';
557 2 100       22 return $value =~ /(1|on|yes|true)/i ? 1 : 0;
558             }
559              
560 23         43 my $ref = ref $value;
561 23         29 my @ret;
562 23 100       58 my $arr = $ref ? $value : [$value];
563              
564 23 100 100     287 croak __PACKAGE__ . ": variable '$vname' should be an array$file"
565             if $ref && $ref ne 'ARRAY';
566              
567 22 100       170 if ( $type eq 'S' ) {
568 8         26 @ret = @$arr;
569             }
570             else {
571 14         34 foreach $value (@$arr) {
572 26         71 $value =~ s/^\s+//;
573 26         54 $value =~ s/\s+$//;
574              
575 26         91 my @values = split /\s*,\s*/, $value;
576              
577 26         47 foreach $value (@values) {
578 32 100       3410 croak __PACKAGE__
579             . ": element '$value' of variable '$vname' is not a prooper $types{$type}->{d}$file"
580             if $value !~ /$types{$type}->{e}/i; #}"}"
581              
582 31         1257 push @ret, $value;
583             }
584             }
585             }
586              
587 21         99 return [ map $self->convert_value( $_, $vname ), @ret ];
588             }
589              
590             sub syntax_check_get_type {
591 68     68 0 96 my ($syntax) = @_;
592              
593 68         340 foreach (qw( A B D E I N T )) {
594 372 100       5277 return $_ if $syntax =~ /$_/;
595             }
596              
597 22         104 return 'S';
598             }
599              
600             my %back_slash = (
601             36 => 36,
602             92 => 92,
603             97 => 7,
604             98 => 8,
605             102 => 12,
606             110 => 10,
607             114 => 13,
608             116 => 9,
609             118 => 11,
610             );
611              
612             sub convert_value {
613 150     150 0 404 my ( $self, $value, $vname ) = @_;
614              
615 150         822 my @arr = unpack 'C*', $value;
616 150         275 my @ret;
617              
618 150         382 while ( my $c = shift @arr ) {
619 637 100 100     2648 if ( $c == 92 && scalar @arr ) { # \
    100 100        
      66        
620 12         16 my $n = shift @arr;
621              
622 12 100       31 if ( $back_slash{$n} ) {
623 11         127 push @ret, $back_slash{$n};
624             }
625             else {
626 1         5 push @ret, 92, $n;
627             }
628             }
629             elsif ( $c == 36 && scalar @arr && $arr[0] == 123 ) { # $
630 11         15 my $i;
631             my $v;
632 0         0 my @var_name;
633              
634 11   100     65 for ( $i = 1 ; $i < scalar @arr && $arr[$i] != 125 ; ++$i ) {
635 39         350 push @var_name, $arr[$i];
636             }
637              
638 11 100       448 croak __PACKAGE__
639             . ": systax error in inline variable substitution for value '$value' for variable '$vname'"
640             if $i == scalar @arr;
641 9 100       26 croak __PACKAGE__
642             . ": can't do inline variable substitution for variable '$vname' when reference to root node was lost"
643             unless $self->opt->root;
644              
645 8         69 eval {
646 8         25 $v = $self->opt->root->get( split /->/, pack 'C*', @var_name );
647             };
648              
649 8 100       474 croak "$@ during inline variable sostitution for variable '$vname'"
650             if $@;
651 5 100       144 croak __PACKAGE__
652             . ": can't use node or array variable in inline variable sostitution for variable '$vname'"
653             if ref $v;
654              
655 4 100       23 $v = '' unless defined $v;
656 4         8 push @ret, unpack 'C*', $v;
657 4         21 splice @arr, 0, $i + 1;
658             }
659             else {
660 614         1830 push @ret, $c;
661             }
662             }
663              
664 143         943 return pack 'C*', @ret;
665             }
666              
667             package Config::General::Hierarchical::Options;
668              
669 5     5   70365 use base 'Class::Accessor::Fast';
  5         14  
  5         1587  
670              
671             my @options = qw( inherits root undefined wild );
672             our %options = map( ( $_ => 1 ), @options );
673              
674             __PACKAGE__->mk_accessors( @options, qw( files general struct ) );
675              
676             package Config::General::Hierarchical::Value;
677              
678 5     5   29 use base 'Class::Accessor::Fast';
  5         13  
  5         666  
679              
680             __PACKAGE__->mk_accessors(qw( file value ));
681              
682             1;
683              
684             __END__