File Coverage

blib/lib/Net/MAC.pm
Criterion Covered Total %
statement 265 300 88.3
branch 96 120 80.0
condition 40 59 67.8
subroutine 35 38 92.1
pod 3 5 60.0
total 439 522 84.1


line stmt bran cond sub pod time code
1             # Net::MAC - Perl extension for representing and manipulating MAC addresses
2             # Copyright (C) 2005-2008 Karl Ward
3             #
4             # This program is free software; you can redistribute it and/or modify
5             # it under the terms of the GNU General Public License as published by
6             # the Free Software Foundation; either version 2 of the License, or
7             # (at your option) any later version.
8             #
9             # This program is distributed in the hope that it will be useful,
10             # but WITHOUT ANY WARRANTY; without even the implied warranty of
11             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12             # GNU General Public License for more details.
13             #
14             # You should have received a copy of the GNU General Public License
15             # along with this program; if not, write to the Free Software
16             # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17              
18             package Net::MAC;
19             BEGIN {
20 5     5   149160 $Net::MAC::VERSION = '2.103622';
21             }
22              
23 5     5   142 use 5.006000;
  5         17  
  5         205  
24 5     5   32 use strict;
  5         9  
  5         195  
25 5     5   32 use Carp;
  5         11  
  5         637  
26 5     5   31 use warnings;
  5         9  
  5         641  
27             use overload
28 439     439   2261 '""' => sub { return $_[0]->get_mac(); },
29 5         64 '==' => \&_compare_value,
30             '!=' => \&_compare_value_ne,
31             'eq' => \&_compare_string,
32 5     5   8861 'ne' => \&_compare_string_ne;
  5         5658  
33              
34             our $AUTOLOAD;
35              
36             # Constructor.
37             sub new {
38 728     728 1 10840 my ( $class, %arg ) = @_;
39 728         2179 my ($self) = {}; # Anonymous hash
40 728         1542 bless( $self, $class ); # Now the hash is an object
41 728 50       1658 if (%arg) {
42 728         2025 $self->_init(%arg);
43             }
44 728         1584 $self->_discover();
45 728         2055 return ($self);
46             }
47              
48             { # Closure for class data and class methods
49              
50             #
51             # CLASS DATA
52             #
53             # These are the valid private attributes of the object, with their
54             # default values, if applicable.
55             my %_attrs = (
56             '_mac' => undef,
57             '_base' => 16,
58             '_delimiter' => ':',
59             '_bit_group' => 48,
60             '_zero_padded' => 1,
61             '_case' => 'upper', # FIXME: does IEEE specify upper?
62             '_groups' => undef,
63             '_internal_mac' => undef,
64             '_die' => 1, # die() on invalid MAC address format
65             '_error' => undef,
66             '_verbose' => 0
67             );
68              
69             # new formats supplied by the user are stored here
70             my %_user_format_for = ();
71              
72             # Preset formats we will accept for use by ->convert, via ->as_foo
73             my %_format_for = (
74             Cisco => {
75             base => 16,
76             bit_group => 16,
77             delimiter => '.',
78             },
79             IEEE => {
80             base => 16,
81             bit_group => 8,
82             delimiter => ':',
83             zero_padded => 1,
84             case => 'upper',
85             },
86             Microsoft => {
87             base => 16,
88             bit_group => 8,
89             delimiter => '-',
90             case => 'upper',
91             },
92             Sun => {
93             base => 16,
94             bit_group => 8,
95             delimiter => ':',
96             zero_padded => 0,
97             case => 'lower'
98             }
99             );
100              
101             #
102             # CLASS METHODS
103             #
104             # Returns a copy of the instance.
105             sub _clone {
106 0     0   0 my ($self) = @_;
107 0         0 my ($clone) = {%$self}; # No need for deep copying here.
108 0         0 bless( $clone, ref $self );
109 0         0 return ($clone);
110             }
111              
112             # Verify that an attribute is valid (called by the AUTOLOAD sub)
113             sub _accessible {
114 2024     2024   2899 my ( $self, $name ) = @_;
115 2024 50       4468 if ( exists $_attrs{$name} ) {
116              
117             #$self->verbose("attribute $name is valid");
118 2024         9734 return 1;
119             }
120 0         0 else { return 0; }
121             }
122              
123             # Initialize the object (only called by the constructor)
124             sub _init {
125 728     728   1547 my ( $self, %arg ) = @_;
126 728 50       1727 if ( defined $arg{'verbose'} ) {
127 0         0 $self->{'_verbose'} = $arg{'verbose'};
128 0         0 delete $arg{'verbose'};
129             }
130              
131             # Set the '_die' attribute to default at the first
132 728         1503 $self->_default('die');
133              
134             # passed a "format" as shorthand for the specific vars
135 728 100       2188 if (exists $arg{'format'}) {
136 1         26 my $f;
137              
138 1 50       5 $f = $_format_for{$arg{'format'}}
139             if exists $_format_for{$arg{'format'}};
140 1 50       5 $f = $_user_format_for{$arg{'format'}}
141             if exists $_user_format_for{$arg{'format'}};
142              
143 1 50 33     24 %arg = (%arg, %$f)
144             if (defined $f and ref $f eq 'HASH');
145              
146 1         3 delete $arg{'format'};
147             }
148              
149 728         2641 foreach my $key ( keys %_attrs ) {
150 8008         23643 $key =~ s/^_+//;
151 8008 100 66     22900 if ( ( defined $arg{$key} ) && ( $self->_accessible("_$key") ) ) {
152 805         2692 $self->verbose("setting \"$key\" to \"$arg{$key}\"");
153 805         2778 $self->{"_$key"} = $arg{$key};
154             }
155             }
156 728         2519 my ($mesg) = "initialized object into class " . ref($self);
157 728         1490 $self->verbose($mesg);
158 728         1498 return (1);
159             }
160              
161             # Set an attribute to its default value
162             sub _default {
163 1157     1157   1547 my ( $self, $key ) = @_;
164 1157 50 33     3475 if ( $self->_accessible("_$key") && $_attrs{"_$key"} ) {
165 1157         4644 $self->verbose( "setting \"$key\" to default value \""
166             . $_attrs{"_$key"}
167             . "\"" );
168 1157         3774 $self->{"_$key"} = $_attrs{"_$key"};
169 1157         1902 return (1);
170             }
171             else {
172 0         0 $self->verbose("no default value for attribute \"$key\"");
173 0         0 return (0); # FIXME: die() here?
174             }
175             }
176              
177             sub _format {
178 16     16   32 my ( $self, $identifier ) = @_;
179              
180             # built-ins first
181 16 100 66     96 if (exists $_format_for{$identifier}
182             and ref $_format_for{$identifier} eq 'HASH') {
183 14         14 return %{$_format_for{$identifier}};
  14         113  
184             }
185              
186             # then user-supplied
187 2 50 33     12 if (exists $_user_format_for{$identifier}
188             and ref $_user_format_for{$identifier} eq 'HASH') {
189 2         3 return %{$_user_format_for{$identifier}};
  2         10  
190             }
191              
192 0         0 return (undef);
193             }
194              
195             # program in a new custom MAC address format supplied by the user
196             sub _set_format_for {
197 2     2   5 my ($self, $identifier, $format) = @_;
198 2 50 33     14 croak "missing identifier for custom format\n"
199             unless defined $identifier and length $identifier;
200 2 100 66     225 croak "missing HASH ref custom format\n"
201             unless defined $format and ref $format eq 'HASH';
202              
203 1         5 $_user_format_for{$identifier} = $format;
204             }
205              
206             } # End closure
207              
208             # program in a new custom MAC address format supplied by the user
209 2     2 1 909 sub set_format_for { goto &_set_format_for }
210              
211             # Automatic accessor methods via AUTOLOAD
212             # See Object Oriented Perl, 3.3, Damian Conway
213             sub Net::MAC::AUTOLOAD {
214 5     5   9535 no strict 'refs';
  5         191  
  5         7225  
215 68     68   677 my ( $self, $value ) = @_;
216 68 100 66     344 if ( ( $AUTOLOAD =~ /.*::get(_\w+)/ ) && ( $self->_accessible($1) ) ) {
217              
218             #$self->verbose("get$1 method");
219 37         77 my $attr_name = $1;
220 37     16126   122 *{$AUTOLOAD} = sub { return $_[0]->{$attr_name} };
  37         108  
  16126         74912  
221 37         1379 return ( $self->{$attr_name} );
222             }
223 31 100 66     180 if ( $AUTOLOAD =~ /.*::set(_\w+)/ && $self->_accessible($1) ) {
224 25         48 my $attr_name = $1;
225 25     2700   80 *{$AUTOLOAD} = sub { $_[0]->{$attr_name} = $_[1]; return; };
  25         87  
  2700         6372  
  2700         3454  
226 25         898 $self->{$1} = $value;
227 25         47 return;
228             }
229 6 50 33     53 if ( $AUTOLOAD =~ /.*::as_(\w+)/ && $_[0]->_format($1) ) {
230 6         13 my $fmt = $1;
231 6     4   21 *{$AUTOLOAD} = sub { return $_[0]->convert( $_[0]->_format($fmt) ) };
  6         23  
  4         16  
232 6         20 return ( $self->convert( $_[0]->_format($fmt) ) );
233             }
234 0         0 croak "No such method: $AUTOLOAD";
235             }
236              
237             # Just for kicks, report an error if we know of one.
238             sub DESTROY {
239 728     728   127986 my ($self) = @_;
240 728         1348 my $error = $self->get_error();
241 728 100       4014 if ($error) {
242 15         58 $self->verbose("Net::MAC detected an error: $error");
243 15         64 return (1);
244             }
245             }
246              
247             # Discover the metadata for this MAC, using hints if necessary
248             sub _discover {
249 728     728   956 my ($self) = @_;
250 728         1245 my $mac = $self->get_mac();
251              
252             # Check for undefined MAC or invalid characters
253 728 50       4146 if ( !( defined $mac ) ) {
    100          
    100          
254 0         0 $self->error(
255             "discovery of MAC address metadata failed, no MAC address supplied"
256             );
257             }
258             elsif ( !( $mac =~ /[a-fA-F0-9]/ ) ) { # Doesn't have hex/dec numbers
259 8         23 $self->error(
260             "discovery of MAC address metadata failed, no meaningful characters in $mac"
261             );
262             }
263             # XXX: this isn't a very effective check for anything
264             elsif ( $mac =~ /[g-z]/i ) {
265 1         5 $self->error(
266             "discovery of MAC address metadata failed, invalid characters in MAC address \"$mac\""
267             );
268             }
269              
270 728 100       1523 unless ( $self->get_delimiter() ) { $self->_find_delimiter(); }
  713         1324  
271 728 100       1597 unless ( $self->get_base() ) { $self->_find_base(); }
  707         1312  
272 728 100       1395 unless ( $self->get_bit_group() ) { $self->_find_bit_group(); }
  712         1421  
273 728 100       1481 unless ( $self->get_zero_padded() ) { $self->_find_zero_padded(); }
  726         1338  
274 728         1689 $self->_write_internal_mac();
275 728         1500 $self->_check_internal_mac();
276 728         1203 return (1);
277             }
278              
279             # Find the delimiter for this MAC address
280             sub _find_delimiter {
281 713     713   875 my ($self) = @_;
282 713         1148 my $mac = $self->get_mac();
283             # XXX: why not just look for any non hexadec char?
284 713 100       2258 if ( $mac =~ m/([^a-zA-Z0-9]+)/ ) { # Found a delimiter
285 294         661 $self->set_delimiter($1);
286 294         999 $self->verbose("setting attribute \"delimiter\" to \"$1\"");
287 294         536 return (1);
288             }
289             else {
290 419         933 $self->set_delimiter(undef);
291 419         799 $self->verbose("setting attribute \"delimiter\" to undef");
292 419         757 return (1);
293             }
294 0         0 $self->error("internal Net::MAC failure for MAC \"$mac\"");
295 0         0 return (0); # Bizarre failure if we get to this line.
296             }
297              
298             # Find the numeric base for this MAC address
299             sub _find_base {
300 707     707   867 my ($self) = @_;
301 707         1137 my $mac = $self->get_mac();
302             # XXX this will fail for 00:00:00:00:00:00 ??
303 707 100       2212 if ( $mac =~ /[a-fA-F]/ ) {
304             # It's hexadecimal
305 688         1340 $self->set_base(16);
306 688         1095 return (1);
307             }
308 19         94 my @groups = split( /[^a-zA-Z0-9]+/, $mac );
309 19         26 my $is_decimal = 0;
310 19         32 foreach my $group (@groups) {
311 47 100       100 if ( length($group) == 3 ) {
312              
313             # It's decimal, sanity check it
314 15         20 $is_decimal = 1;
315 15 100       36 if ( $group > 255 ) {
316 3         11 $self->error("invalid decimal MAC \"$mac\"");
317 3         8 return (0);
318             }
319             }
320             }
321 16 100       36 if ($is_decimal) {
322 4         9 $self->set_base(10);
323 4         10 return (1);
324             }
325              
326             # There are no obvious indicators, so we'll default the value
327 12         23 $self->_default('base');
328 12         23 return (1);
329             }
330              
331             # Find the bit grouping for this MAC address
332             sub _find_bit_group {
333 712     712   841 my ($self) = @_;
334 712         1201 my $mac = $self->get_mac();
335 712 100       1964 if ( $mac =~ m/([^a-zA-Z0-9]+)/ ) { # Found a delimiter
336 293 100       931 my $delimiter = ($1 eq ' ' ? '\s' : '\\'. $1);
337 293         3744 my @groups = split( /$delimiter/, $mac );
338 293 50 66     1858 if ( ( @groups > 3 ) && ( @groups % 2 ) ) {
    100          
339 0         0 $self->error("invalid MAC address format: $mac");
340             }
341             elsif (@groups) {
342 5     5   5535 use integer;
  5         55  
  5         27  
343 286         351 my $n = @groups;
344 286         447 my $t_bg = 48 / $n;
345 286 100 100     860 if ( ( $t_bg == 8 ) || ( $t_bg == 16 ) ) {
346 284         672 $self->set_bit_group($t_bg);
347 284         874 $self->verbose(
348             "setting attribute \"bit_group\" to \"$t_bg\"");
349 284         861 return (1);
350             }
351             else {
352 2         7 $self->error("invalid MAC address format: $mac");
353 2         6 return (0);
354             }
355             }
356             }
357             else { # No delimiter, bit grouping is 48 bits
358             # Sanity check the length of the MAC address in characters
359 419 100       801 if ( length($mac) != 12 ) {
360 2         14 $self->error(
361             "invalid MAC format, not 12 characters in hexadecimal MAC \"$mac\""
362             );
363 2         5 return (0);
364             }
365             else {
366 417         797 $self->_default('bit_group');
367 417         799 return (1);
368             }
369             }
370              
371             # If we get here the MAC is invalid or there's a bug in Net::MAC
372 7         20 $self->error("invalid MAC address format \"$mac\"");
373             }
374              
375             # FIXME: untested
376             # Find whether this MAC address has zero-padded bit groups
377             sub _find_zero_padded {
378 726     726   909 my ($self) = @_;
379              
380             # Zero-padding is only allowed for 8 bit grouping
381 726 100 100     1207 unless ( $self->get_bit_group() && ( $self->get_bit_group() == 8 ) ) {
382 453         786 return (0); # False
383             }
384 273         545 my $delimiter = $self->get_delimiter();
385 273 100       669 if ( $delimiter eq ' ' ) { $delimiter = '\s'; }
  54         85  
386 273         2004 my @groups = split( /\Q$delimiter\E/, $self->get_mac() );
387 273         598 foreach my $group (@groups) {
388 1161 100       2631 if ( $group =~ /^0./ ) {
389 54         130 $self->set_zero_padded(1);
390 54         149 return (1); # True, zero-padded group.
391             }
392             }
393 219         511 $self->set_zero_padded(0);
394 219         499 return (0); # False, if we got this far.
395             }
396              
397             # Write an internal representation of the MAC address.
398             # This is mainly useful for conversion between formats.
399             sub _write_internal_mac {
400 728     728   874 my ($self) = @_;
401 728         1737 my $mac = $self->get_mac();
402 728         11873 $mac =~ s/(\w)/\l$1/g;
403              
404             #my @groups = $self->get_groups();
405 728         973 my @groups;
406 728         1279 my $delimiter = $self->get_delimiter();
407 728 100       1269 if ($delimiter) {
408 309 100       719 $delimiter = ($delimiter eq ' ' ? '\s' : '\\'. $delimiter);
409 309         2708 @groups = split( /$delimiter/, $mac );
410             }
411 419         940 else { @groups = $mac; }
412              
413             # Hex base
414 728 100 100     1542 if ( ( defined $self->get_base() ) && ( $self->get_base() == 16 ) ) {
415 716         736 my $bit_group;
416 716 100       1177 if ( defined $self->get_bit_group() ) {
417 706         1268 $bit_group = $self->get_bit_group();
418             }
419 10         15 else { $bit_group = 48; }
420 716         1400 my ($chars) = $bit_group / 4;
421 716         723 my ($internal_mac);
422 716         1039 foreach my $element (@groups) {
423 2081         7740 my $format = '%0' . $chars . 's';
424 2081         5761 $internal_mac .= sprintf( $format, $element );
425             }
426 716         1790 $self->set_internal_mac($internal_mac);
427 716         2044 return (1);
428             }
429             else { # Decimal base
430 12 100       29 if ( @groups == 6 ) { # Decimal addresses can only have octet grouping
431 11         15 my @hex_groups;
432 11         19 foreach my $group (@groups) {
433 66         140 my $hex = sprintf( "%02x", $group );
434 66         106 push( @hex_groups, $hex );
435             }
436 11         28 my $imac = join( '', @hex_groups );
437 11         25 $self->set_internal_mac($imac);
438 11         39 return (1);
439             }
440             else {
441 1         5 $self->error("unsupported MAC address format \"$mac\"");
442 1         3 return (0);
443             }
444             }
445 0         0 $self->error("internal Net::MAC failure for MAC \"$mac\"");
446 0         0 return (0); # FIXME: die() here?
447             }
448              
449             # Check the internal MAC address for errors (last check)
450             sub _check_internal_mac {
451 728     728   988 my ($self) = @_;
452 728 100       1329 if ( !defined( $self->get_internal_mac() ) ) {
    100          
453 8         14 my $mac = $self->get_mac();
454 8         21 $self->error("invalid MAC address \"$mac\"");
455 8         12 return (0);
456             }
457             elsif ( length( $self->get_internal_mac() ) != 12 ) {
458 4         10 my $mac = $self->get_mac();
459 4         14 $self->error("invalid MAC address \"$mac\"");
460 4         7 return (0);
461             }
462 716         1064 else { return (1) }
463             }
464              
465             # Convert a MAC address object into a different format
466             sub convert {
467 16     16 1 799 my ( $self, %arg ) = @_;
468 16         36 my $imac = $self->get_internal_mac();
469 16         27 my @groups;
470 16   100     49 my $bit_group = $arg{'bit_group'} || 8; # not _default value
471 16         21 my $offset = 0;
472 5     5   6044 use integer;
  5         10  
  5         22  
473 16         22 my $size = $bit_group / 4;
474 5     5   154 no integer;
  5         9  
  5         81  
475              
476 16         56 while ( $offset < length($imac) ) {
477 76         106 my $group = substr( $imac, $offset, $size );
478 76 100 100     317 if ( ( $bit_group == 8 )
      100        
479             && ( exists $arg{zero_padded} )
480             && ( $arg{zero_padded} == 0 ) )
481             {
482 12         25 $group =~ s/^0//;
483             }
484 76         117 push( @groups, $group );
485 76         160 $offset += $size;
486             }
487              
488             # Convert to base 10 if necessary
489 16 100 100     73 if ( ( exists $arg{'base'} ) && ( $arg{'base'} == 10 ) )
490             { # Convert to decimal base
491 1         2 my @dec_groups;
492 1         2 foreach my $group (@groups) {
493 6         9 my $dec_group = hex($group);
494 6         11 push( @dec_groups, $dec_group );
495             }
496 1         5 @groups = @dec_groups;
497             }
498 16         20 my $mac_string;
499 16 100       42 if ( exists $arg{delimiter} ) {
    50          
500              
501             #warn "\nconvert delimiter $arg{'delimiter'}\n";
502             #my $delimiter = $arg{'delimiter'};
503             #$delimiter =~ s/(:|\-|\.)/\\$1/;
504 14         42 $mac_string = join( $arg{'delimiter'}, @groups );
505              
506             #warn "\nconvert groups @groups\n";
507             }
508             elsif ($bit_group != 48) {
509             # use default delimiter
510 2         6 $mac_string = join( ':', @groups );
511             }
512             else {
513 0         0 $mac_string = join( '', @groups );
514             }
515              
516 16 100 66     75 if ( exists $arg{case} && $arg{case} =~ /^(upper|lower)$/ ) {
517 6         10 for ($mac_string) {
518 6 100       28 $_ = $arg{case} eq 'upper' ? uc : lc;
519             }
520             }
521              
522             # Construct the argument list for the new Net::MAC object
523 16         41 $arg{'mac'} = $mac_string;
524              
525             # foreach my $test (keys %arg) {
526             # warn "\nconvert arg $test is $arg{$test}\n";
527             # }
528 16         65 my $new_mac = Net::MAC->new(%arg);
529 16         109 return ($new_mac);
530             }
531              
532             # Overloading the == operator (numerical comparison)
533             sub _compare_value {
534 400     400   716 my ( $arg_1, $arg_2, $reversed ) = @_;
535 400         448 my ( $mac_1, $mac_2 );
536 400 50       2162 if ( UNIVERSAL::isa( $arg_2, 'Net::MAC' ) ) {
537 0         0 $mac_2 = $arg_2->get_internal_mac();
538             }
539             else {
540 400         1006 my $temp = Net::MAC->new( mac => $arg_2 );
541 400         758 $mac_2 = $temp->get_internal_mac();
542             }
543 400         902 $mac_1 = $arg_1->get_internal_mac();
544 400 50       866 if ( $mac_1 eq $mac_2 ) { return (1); }
  400         1722  
545 0         0 else { return (0); }
546             }
547              
548             # Overloading the != operator (numeric comparison)
549             sub _compare_value_ne {
550 0     0   0 my ( $arg_1, $arg_2 ) = @_;
551 0 0       0 if ( $arg_1 == $arg_2 ) { return (0); }
  0         0  
552 0         0 else { return (1); }
553             }
554              
555             # Overloading the eq operator (string comparison)
556             sub _compare_string {
557 43     43   4186 my ( $arg_1, $arg_2, $reversed ) = @_;
558 43         61 my ( $mac_1, $mac_2 );
559 43 50       244 if ( UNIVERSAL::isa( $arg_2, 'Net::MAC' ) ) {
560 0         0 $mac_2 = $arg_2->get_mac();
561             }
562             else {
563 43         115 my $temp = Net::MAC->new( mac => $arg_2 );
564 43         123 $mac_2 = $temp->get_mac();
565             }
566 43         120 $mac_1 = $arg_1->get_mac();
567 43 50       82 if ( $mac_1 eq $mac_2 ) { return (1); }
  43         413  
568 0         0 else { return (0); }
569             }
570              
571             # Overloading the ne operator (string comparison)
572             sub _compare_string_ne {
573 0     0   0 my ( $arg_1, $arg_2 ) = @_;
574 0 0       0 if ( $arg_1 eq $arg_2 ) { return (0); }
  0         0  
575 0         0 else { return (1); }
576             }
577              
578             # Print verbose messages about internal workings of this class
579             sub verbose {
580 3702     3702 0 4949 my ( $self, $message ) = @_;
581 3702 50 33     17810 if ( ( defined($message) ) && ( $self->{'_verbose'} ) ) {
582 0         0 chomp($message);
583 0         0 print "$message\n";
584             }
585             }
586              
587             # carp(), croak(), or ignore errors, depending on the attributes of the object.
588             # If the object is configured to stay alive despite errors, this method will
589             # store the error message in the '_error' attribute of the object, accessible
590             # via the get_error() method.
591             sub error {
592 36     36 0 54 my ( $self, $message ) = @_;
593 36 50       71 if ( $self->get_die() ) { # die attribute is set to 1
    50          
594 0         0 croak $message;
595             }
596             elsif ( $self->get_verbose() ) { # die attribute is set to 0
597 0         0 $self->set_error($message);
598 0         0 carp $message; # Be verbose, carp() the message
599             }
600             else { # die attribute is set to 0, verbose is set to 0
601 36         63 $self->set_error($message); # Just store the error
602             }
603 36         162 return (1);
604             }
605              
606             1; # Necessary for usage statement
607              
608             # ABSTRACT: Perl extension for representing and manipulating MAC addresses
609              
610              
611             __END__