File Coverage

examples/perl-reversion
Criterion Covered Total %
statement 245 264 92.8
branch 80 108 74.0
condition 7 12 58.3
subroutine 40 41 97.5
pod n/a
total 372 425 87.5


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2             # vim: ts=4 sts=4 sw=4:
3             #
4             # perl-reversion
5             #
6             # Update embedded version strings in Perl source
7 78     78   436889 use utf8;
  78         26367  
  78         679  
8 78     78   3525 use v5.10;
  78         282  
9              
10 78     78   918 use strict;
  78         147  
  78         1843  
11 78     78   302 use warnings;
  78         193  
  78         3657  
12 78     78   43399 use Perl::Version;
  78         256  
  78         3458  
13 78     78   610 use Carp qw(croak);
  78         124  
  78         5475  
14 78     78   59458 use Getopt::Long;
  78         1098698  
  78         398  
15 78     78   63405 use Pod::Usage;
  78         6179567  
  78         11446  
16 78     78   862 use File::Spec;
  78         134  
  78         2952  
17 78     78   417 use File::Basename;
  78         165  
  78         7492  
18 78     78   43932 use IO::File;
  78         631661  
  78         496848  
19              
20             # Files that suggest that we have a project directory. The scores next
21             # to each are summed for each candidate directory. The first directory
22             # with a score >= 1.0 is assumed to be the project home.
23              
24 78         12027085 my %PROJECT_SIGNATURE = (
25             'Makefile.PL' => 0.4,
26             'Build.PL' => 0.4,
27             'dist.ini' => 0.4,
28             'MANIFEST' => 0.4,
29             't/' => 0.4,
30             'lib/' => 0.4,
31             'Changes' => 0.4,
32             'xt/' => 0.4,
33             );
34              
35 78         394 my $MODULE_RE = qr{ [.] pm $ }x;
36 78         821 my $SCRIPT_RE = qr/ \p{IsWord}+ /x; # filenames
37              
38             # Places to look for files / directories when processing a project
39              
40 78         820 my %CONSIDER = (
41             'lib/' => { like => $MODULE_RE },
42             'bin/' => { like => $SCRIPT_RE },
43             'script/' => { like => $SCRIPT_RE },
44             'README' => {},
45             'META.yml' => {},
46             );
47              
48             # Maximum number of levels above current directory to search for
49             # project home.
50              
51 78         200 my $MAX_UP = 5;
52              
53             # Subroutines to identify file types
54             my @MAGIC = (
55             {
56             name => 'perl',
57             test => sub {
58 79     79   186 my ( $name, $info ) = @_;
59 79 100       879 return 1 if $name =~ m{ [.] (?i: pl | pm | t | xs ) $ }x;
60 25         56 my $lines = $info->{lines};
61 25 50 33     186 return 1 if @$lines && $lines->[0] =~ m{ ^ \#\! .* perl }ix;
62 25         88 return;
63             },
64             },
65             {
66             name => 'meta',
67             test => sub {
68 25     25   62 my ( $name, $info ) = @_;
69 25         1885 return basename( $name ) eq 'META.yml';
70             },
71             },
72             {
73             name => 'plain',
74             test => sub {
75 12     12   26 my ( $name, $info ) = @_;
76 12         572 return -T $name;
77             },
78             }
79 78         1331 );
80              
81 78         204 my $man = 0;
82 78         155 my $help = 0;
83 78         188 my $quiet = 0;
84 78         156 my $bump = undef;
85 78         718 my $current = undef;
86 78         507 my $set = undef;
87 78         328 my $dryrun = undef;
88 78         250 my $force_to = undef;
89 78         228 my @dir_skip = ();
90              
91 78         507 my %BUMP = (
92             bump => 'auto', # original -bump behavior
93             'bump-revision' => 0,
94             'bump-version' => 1,
95             'bump-subversion' => 2,
96             'bump-alpha' => 3,
97             );
98              
99             GetOptions(
100             'help|?' => \$help,
101             'man' => \$man,
102             'current=s' => \$current,
103             'set=s' => \$set,
104             'dirskip=s' => \@dir_skip,
105             (
106             map {
107 390         547 my $opt = $_;
108             $_ => sub {
109 18 50   18   27472 if ( defined $bump ) {
110 0         0 die "Please specify only one -bump option\n";
111             }
112 18         189 $bump = $BUMP{$opt};
113             }
114 390         1128 } keys %BUMP
115             ),
116             (
117             map {
118 78 50       394 my $opt = $_;
  234         382  
119             $_ => sub {
120 10 50   10   17868 if ( defined $force_to ) {
121 0         0 die
122             "Please specify only one of -normal, -numify, or -stringify\n";
123             }
124 10         144 $force_to = $opt;
125             }
126 234         1081 } qw(normal numify stringify)
127             ),
128             'dryrun' => \$dryrun,
129             'quiet' => \$quiet,
130             ) or pod2usage( 2 );
131              
132 78 50       73104 pod2usage( 1 ) if $help;
133 78 50       259 pod2usage( -exitstatus => 0, -verbose => 2 ) if $man;
134              
135 78 50 66     384 die "Please specify either -set or -bump, not both\n"
136             if $set && $bump;
137              
138             # Directories to skip during expansion
139 78         360 my @skip = ( qw( .svn .git .github blib CVS .DS_Store ), @dir_skip );
140              
141             # this slightly changes the way it was done before. It's still a
142             # regex, but I fixed the regex for precedence with anchors, and
143             # quotemeta everything.
144 78         193 my $SKIP = '^( ' . join( ' | ', map { quotemeta($_) } @skip ) . ' )$';
  468         1104  
145             #note( "Regex is $SKIP\n" );
146 78         3244 $SKIP = qr/$SKIP/x;
147              
148 78 100       690 my @files = @ARGV ? expand_dirs( @ARGV ) : find_proj_files();
149              
150 78 50       308 die "Can't find any files to process. Try naming some\n",
151             "directories and/or files on the command line.\n"
152             unless @files;
153              
154 78 50       223 if ( my @missing = grep { !-e $_ } @files ) {
  79         1031  
155 0         0 die "Can't find ", conjunction_list( 'or', @missing ), "\n";
156             }
157              
158 78         209 my %documents = map { $_ => {} } @files;
  79         403  
159 78         461 load_all( \%documents );
160              
161 78 50       267 if ( my @bad_type
162 79         407 = grep { !defined $documents{$_}{type} } keys %documents ) {
163 0         0 die "Can't process ", conjunction_list( 'or', @bad_type ), "\n",
164             "I can only process text files\n";
165             }
166              
167 78         349 my $versions = find_versions( \%documents, $current );
168 78         333 my @got = sort keys %$versions;
169              
170 78 100       568 if ( @got == 0 ) {
    50          
171 6 50       0 die "Can't find ", defined $current
172             ? "version string $current\n"
173             : "any version strings\n";
174             }
175             elsif ( @got > 1 ) {
176             die "Found versions ",
177 0         0 conjunction_list( 'and', map { "$versions->{$_}[0]{ver}" } @got ),
  0         0  
178             ". Please use\n",
179             "the --current option to specify the current version\n";
180             }
181              
182 72         149 my $new_ver;
183 72 100       282 if ( $set ) {
    100          
184 19         86 $new_ver = Perl::Version->new( $set );
185             }
186             elsif ( defined $bump ) {
187 18         57 $new_ver = $versions->{ $got[0] }[0]{ver};
188 18 100       63 if ( $bump eq 'auto' ) {
189 12 100       56 if ( $new_ver->is_alpha ) {
190 2         9 $new_ver->inc_alpha;
191             }
192             else {
193 10         42 my $pos = $new_ver->components - 1;
194 10         40 $new_ver->increment( $pos );
195             }
196             }
197             else {
198 6         39 my $pos = $new_ver->components - 1;
199 6 50       21 if ( $bump > $pos ) {
200 6         41 my %NAME = (
201             0 => 'revision',
202             1 => 'version',
203             2 => 'subversion',
204             3 => 'alpha',
205             );
206 6         15 my $name = $NAME{$bump};
207 6         33 die "Cannot -bump-$name -- version $new_ver does not have "
208             . "'$name' component.\n"
209             . "Use -set if you intended to add it.\n";
210             }
211 0         0 $new_ver->increment( $bump );
212             }
213             }
214             else {
215 35         102 my $current_ver = $versions->{ $got[0] }[0]{ver};
216 35 50       150 $current_ver = $current_ver->$force_to if $force_to;
217 35         153 note( "Current project version is $current_ver\n" );
218             }
219              
220 66 100       202 if ( defined $new_ver ) {
221 31         149 set_versions( \%documents, $versions, $new_ver, $force_to );
222 31         242 save_all( \%documents );
223             }
224              
225             sub version_re_perl_pack {
226 156     156   320 my $ver_re = shift;
227              
228             return
229 156         18724 qr{ ^(\s* package \s+ (?: \w+ (?: (?: :: | ' ) \w+ )* \s+ ))
230             $ver_re
231             ( .* \s* ) \z }x;
232             }
233              
234              
235             sub version_re_perl {
236 78     78   264 my $ver_re = shift;
237              
238             return
239 78         10941 qr{ ^ ( .*? [\$\*] (?: \w+ (?: :: | ' ) )* VERSION \s* = \D*? )
240             $ver_re
241             ( .* \s*) \z }x;
242             }
243              
244             sub version_re_test {
245 78     78   270 my $ver_re = shift;
246 78         5696 return qr{ ^ ( .*? use_ok .*? ) $ver_re ( .* \s*) \z }x;
247             }
248              
249              
250             sub version_re_pod {
251 78     78   155 my $ver_re = shift;
252              
253 78         5563 return qr{ ^ ( .*? (?i: version ) .*? ) $ver_re ( .* \s*) \z }x;
254             }
255              
256             sub version_re_plain {
257 78     78   216 my $ver_re = shift;
258 78         6387 return qr{ ^ ( .*? ) $ver_re ( .* \s* ) \z }x;
259             }
260              
261             sub version_re_meta {
262 13     13   37 my ( $indent, $ver_re ) = @_;
263 13         1486 return qr{ ^ ( $indent version: \s* ) $ver_re ( \s* ) }x;
264             }
265              
266             sub set_versions {
267 31     31   64 my $docs = shift;
268 31         48 my $versions = shift;
269 31 50       169 my $new_version = shift
270             or die "Internal: no version specified";
271 31         1745 my $force_to = shift;
272              
273 31 100       102 if ( $force_to ) {
274             # the forced formats set their own formats, so override the deatils
275             # in the string we want
276             # https://github.com/briandfoy/perl-version/issues/10
277 10         27 my $alpha_format = $new_version->{format}{alpha};
278 10         54 $new_version = Perl::Version->new( $new_version->$force_to );
279 10         27 $new_version->{format}{alpha} = $alpha_format;
280             }
281              
282 31         140 note( "Setting version to $new_version\n" );
283              
284             # Edit the documents
285 31         120 for my $edits ( values %$versions ) {
286 31         113 for my $edit ( @$edits ) {
287 31         72 my $info = $edit->{info};
288              
289 31 100       199 if ( $force_to ) {
290 10         111 $edit->{ver} = $new_version;
291             }
292             else {
293 21         95 $edit->{ver}->set( $new_version );
294             }
295              
296             $info->{lines}[ $edit->{line} ]
297 31         214 = $edit->{pre} . $edit->{ver} . $edit->{post};
298 31         135 $info->{dirty}++;
299             }
300             }
301             }
302              
303             sub find_version_for_doc {
304 79     79   257 my ( $ver_found, $version, $name, $info, $machine ) = @_;
305              
306 79         504 note( "Scanning $name\n" );
307              
308 79         370 my $state = $machine->{init};
309 79         252 my $lines = $info->{lines};
310              
311             LINE:
312 79         415 for my $ln ( 0 .. @$lines - 1 ) {
313 1378         1475 my $line = $lines->[$ln];
314              
315             # Bail out when we're in a state with no possible actions.
316 1378 100       1606 last LINE unless @$state;
317              
318             STATE: {
319 1373         1031 for my $trans ( @$state ) {
  1423         1337  
320 3212 100       8130 if ( my @match = $line =~ $trans->{re} ) {
321 119 100       384 if ( $trans->{mark} ) {
322 79         1413 my $ver = Perl::Version->new( $2 . $3 . $4 );
323 79 100 66     402 next if defined $version and "$version" ne "$ver";
324 73         127 push @{ $ver_found->{ $ver->normal } },
  73         379  
325             {
326             file => $name,
327             info => $info,
328             line => $ln,
329             pre => $1,
330             ver => $ver,
331             post => $5
332             };
333 73         423 note( " $ver" );
334             }
335              
336 113 100       521 if ( my $code = $trans->{exec} ) {
337 13         44 $code->( $machine, \@match, $line );
338             }
339              
340 113 100       498 if ( my $goto = $trans->{goto} ) {
341 50         87 $state = $machine->{$goto};
342 50         136 redo STATE;
343             }
344             }
345             }
346             }
347             }
348 79         291 note( "\n" );
349             }
350              
351             sub find_versions {
352 78     78   164 my $docs = shift;
353 78         143 my $version = shift;
354              
355 78         160 my $ver_re = Perl::Version::REGEX;
356              
357             # Filetypes that don't have much to say about what the version
358             # might be.
359 78         162 my %uncertain = map { $_ => 1 } qw( plain );
  78         328  
360              
361             my %machines = (
362              
363             # State machine for Perl source
364             perl => {
365             init => [
366             {
367             re => qr{ ^ = (?! cut ) }x,
368             goto => 'pod',
369             },
370             {
371             re => version_re_perl_pack( $ver_re ),
372             mark => 1,
373             },
374             {
375             re => version_re_perl( $ver_re ),
376             mark => 1,
377             },
378             ],
379              
380             # pod within perl
381             pod => [
382             {
383             re => qr{ ^ =head\d\s+VERSION\b }x,
384             goto => 'version',
385             },
386             {
387             re => qr{ ^ =cut }x,
388             goto => 'init',
389             },
390             ],
391              
392             # version section within pod
393             version => [
394             {
395             re => qr{ ^ = (?! head\d\s+VERSION\b ) }x,
396             goto => 'pod',
397             },
398             {
399             re => version_re_test( $ver_re ),
400             mark => 1,
401             },
402             {
403             re => version_re_perl_pack( $ver_re ),
404             mark => 1,
405             },
406             {
407             re => version_re_pod( $ver_re ),
408             mark => 1,
409             },
410              
411             ],
412             },
413              
414             # State machine for plain text. Matches once then loops
415             plain => {
416             init => [
417             {
418             re => version_re_plain( $ver_re ),
419             mark => 1,
420             goto => 'done',
421             }
422             ],
423             done => [],
424             },
425              
426             # State machine for META.yml.
427             meta => {
428             init => [
429             {
430             re => qr{^ (\s*) (?! ---) }x,
431             goto => 'version',
432             exec => sub {
433 13     13   37 my ( $machine, $matches, $line ) = @_;
434             $machine->{version} = [
435             {
436 13         80 re => version_re_meta(
437             '\s{' . length( $matches->[0] ) . '}', $ver_re
438             ),
439             mark => 1,
440             },
441             ];
442             },
443             },
444 78         815 ],
445             },
446             );
447              
448 78         590 my $ver_found = {};
449              
450             my $scan_like = sub {
451 156     156   388 my ( $version, $filter ) = @_;
452 156         1013 while ( my ( $name, $info ) = each %$docs ) {
453 158 100       526 next unless $filter->( $info->{type} );
454             my $machine = $machines{ $info->{type} }
455             or die "Internal: can't find state machine for type ",
456 79 50       384 $info->{type};
457 79         350 find_version_for_doc( $ver_found, $version, $name, $info,
458             $machine );
459             }
460 78         381 };
461              
462 78     79   383 $scan_like->( $version, sub { !$uncertain{ $_[0] } } );
  79         395  
463              
464             # Can we guess what the version is now?
465 78 100       520 unless ( defined $version ) {
466 72         258 my @found = keys %$ver_found;
467             $version = $ver_found->{ $found[0] }[0]{ver}
468 72 100       520 if @found == 1;
469             }
470              
471 78     79   572 $scan_like->( $version, sub { $uncertain{ $_[0] } } );
  79         391  
472              
473 78         1708 return $ver_found;
474             }
475              
476             sub guess_type {
477 79     79   262 my ( $name, $info ) = @_;
478 79         197 for my $try ( @MAGIC ) {
479             return $try->{name}
480 116 100       411 if $try->{test}->( $name, $info );
481             }
482              
483 0         0 return;
484             }
485              
486             sub load_all {
487 78     78   152 my $docs = shift;
488              
489 78         293 for my $doc ( keys %$docs ) {
490              
491             #note( "Loading $doc\n" );
492 79         336 $docs->{$doc} = {
493             lines => read_lines( $doc, ':raw', array_ref => 1 ),
494             dirty => 0,
495             };
496 79         396 $docs->{$doc}{type} = guess_type( $doc, $docs->{$doc} );
497              
498             #note( "Type is ", $docs->{$doc}{type}, "\n" );
499             }
500             }
501              
502             sub read_lines {
503 79     79   490 my( $file, $mode, %args ) = @_;
504              
505 79         326 my @lines;
506              
507 79 50       3554 if( open my $fh, "<$mode", $file ) {
508 79         2654 @lines = <$fh>;
509 79         929 close $fh;
510             }
511              
512 79 50       947 return \@lines if $args{array_ref};
513 0         0 return @lines;
514             }
515              
516             sub save_all {
517 31     31   191 my $docs = shift;
518              
519 31         117 for my $doc ( grep { $docs->{$_}{dirty} } keys %$docs ) {
  31         168  
520 31 50       203 if ( $dryrun ) {
521 0         0 note( "Would save $doc\n" );
522             }
523             else {
524 31         137 note( "Saving $doc\n" );
525 31         56 my $mode = eval { (stat $doc)[2] & 07777 };
  31         777  
526 31 50       4044 open my $fh, '>:raw', $doc or croak "Could not open file $doc: $!\n";
527 31         529 $fh->autoflush(1);
528 31         2095 print $fh @{ $docs->{$doc}{lines} };
  31         1611  
529 31         2098 close $fh;
530 31 50       0 chmod $mode, $doc if defined $mode;
531             }
532             }
533             }
534              
535             sub note {
536 328 100   328   1774 print join( '', @_ ) unless $quiet;
537             }
538              
539             sub find_proj_files {
540 1 50   1   15 if ( my $dir = find_project( File::Spec->curdir ) ) {
541 1         1 my @files = ();
542 1         4 while ( my ( $obj, $spec ) = each %CONSIDER ) {
543 5 100       9 if ( my $got = exists_in( $dir, $obj ) ) {
544             push @files,
545 2   66     16 expand_dirs_matching( $spec->{like} || qr{}, $got );
546             }
547             }
548 1 50       3 unless ( @files ) {
549 0         0 die "I looked at ",
550             conjunction_list( 'and', sort keys %CONSIDER ),
551             " but found no files to process\n";
552             }
553 1         3 return @files;
554             }
555             else {
556 0         0 die "No files / directories specified and I can't\n",
557             "find a directory that looks like a project home.\n";
558             }
559             }
560              
561             sub conjunction_list {
562 0     0   0 my $conj = shift;
563 0         0 my @list = @_;
564 0         0 my $last = pop @list;
565 0 0       0 return $last unless @list;
566 0         0 return join( " $conj ", join( ', ', @list ), $last );
567             }
568              
569             sub expand_dirs {
570 77     77   485 return expand_dirs_matching( qr{}, @_ );
571             }
572              
573             sub expand_dirs_matching {
574 79     79   159 my $match = shift;
575 79         239 my @work = @_;
576 79         176 my @out = ();
577 79         396 while ( my $obj = shift @work ) {
578 235 100       5380 if ( -d $obj ) {
    50          
579 156 50       6028 opendir my $dh, $obj
580             or die "Can't read directory $obj ($!)\n";
581 156         2398 push @work, map { File::Spec->catdir( $obj, $_ ) }
582 156         793 grep { $_ !~ $SKIP }
583 156         5129 grep { $_ !~ /^[.][.]?$/ } readdir $dh;
  468         1829  
584 156         2433 closedir $dh;
585             }
586             elsif ( $obj =~ $match ) {
587 79         360 push @out, $obj;
588             }
589             }
590              
591 79         331 return @out;
592             }
593              
594             sub exists_in {
595 13     13   14 my ( $base, $name ) = @_;
596              
597 13         8 my $try;
598              
599 13 100       35 if ( $name =~ m{^(.+)/$} ) {
600 6         23 $try = File::Spec->catdir( $base, $1 );
601 6 100       106 return unless -d $try;
602             }
603             else {
604 7         41 $try = File::Spec->catfile( $base, $name );
605 7 100       156 return unless -f $try;
606             }
607              
608 8         29 return File::Spec->canonpath( $try );
609             }
610              
611             sub find_dir_like {
612 1     1   2 my $start = shift;
613 1         2 my $max_up = shift;
614 1         1 my $signature = shift;
615              
616 1         3 for ( 1 .. $max_up ) {
617 1         1 my $score = 0;
618 1         5 while ( my ( $file, $weight ) = each %$signature ) {
619 8 100       12 $score += $weight if exists_in( $start, $file );
620             }
621 1 50       9 return File::Spec->canonpath( $start ) if $score >= 1.0;
622 0         0 $start = File::Spec->catdir( $start, File::Spec->updir );
623             }
624              
625 0         0 return;
626             }
627              
628             # Find the project directory
629             sub find_project {
630 1     1   5 return find_dir_like( shift, $MAX_UP, \%PROJECT_SIGNATURE );
631             }
632              
633             __END__
634              
635             =head1 NAME
636              
637             perl-reversion - Manipulate project version numbers
638              
639             =head1 SYNOPSIS
640              
641             perl-reversion [options] [file ...]
642              
643             Options:
644              
645             -help see this summary
646             -man view man page for perl-reversion
647             -bump make the smallest possible increment
648              
649             -bump-revision increment the specified version component
650             -bump-version
651             -bump-subversion
652             -bump-alpha
653              
654             -set <version> set the project version number
655             -current <version> specify the current version
656              
657             -dskip specify a directory not to searched
658             you can specify this multiple times
659              
660             -normal print current version in a specific format OR
661             -numify force versions to be a specific format,
662             -stringify with -set or -bump
663              
664             -dryrun just go through the motions, but don't
665             actually save files
666              
667             =head1 DESCRIPTION
668              
669             A typical distribution of a Perl module has embedded version numbers is
670             a number of places. Typically the version will be mentioned in the
671             README file and in each module's source. For a module the version may
672             appear twice: once in the code and once in the pod.
673              
674             This script makes it possible to update all of these version numbers
675             with a simple command.
676              
677             To update the version numbers of specific files name them on the command
678             line. Any directories will be recursively expanded.
679              
680             If used with no filename arguments perl-reversion will attempt to update
681             README and any files below lib/ in the current project.
682              
683             =head1 OPTIONS
684              
685             =over
686              
687             =item C<< -bump >>
688              
689             Attempt to make the smallest possible increment to the version. The
690             least significant part of the version string is incremented.
691              
692             1 => 2
693             1.1 => 1.2
694             1.1.1 => 1.1.2
695             1.1.1_1 => 1.1.1_2
696              
697             =item C<< -bump-revision >>
698              
699             =item C<< -bump-version >>
700              
701             =item C<< -bump-subversion >>
702              
703             =item C<< -bump-alpha >>
704              
705             Increment the specified version component. Like the C<inc_*> methods of
706             L<Perl::Version>, incrementing a component sets all components to the right of
707             it to zero.
708              
709             =item C<< -set <version> >>
710              
711             Set the version to the specified value. Unless the C<-normal> option is
712             also specified the format of each individual version string will be
713             preserved.
714              
715             =item C<< -current <version> >>
716              
717             Specify the current version. Only matching version strings will
718             be updated.
719              
720             =item C<< -dskip <dir> >>
721              
722             Specify the directory dir not to search. This option can be selected
723             multiple times.
724              
725             =item C<< -normal >>
726              
727             =item C<< -numify >>
728              
729             =item C<< -stringify >>
730              
731             Use a specific formatting, as in L<Perl::Version/Formatting>.
732              
733             Alone, these options control how the current (found) version is displayed.
734              
735             With C<-bump> or C<-set>, also update version strings to have the given
736             formatting, regardless of the version format passed to C<-set> or the current
737             version (for C<-bump>).
738              
739             If none of these options are specified, perl-reversion will preserve the
740             formatting of each individual version string (the same as C<-stringify>).
741              
742             =item C<< -dryrun >>
743              
744             If set, perl-reversion will not save files. Use this to see
745             what gets changed before it actually happens.
746              
747             =back
748              
749             =head1 SOURCE
750              
751             The source is available at:
752              
753             https://github.com/briandfoy/perl-version
754              
755             =head1 AUTHOR
756              
757             Andy Armstrong C<< <andy@hexten.net> >>
758              
759             Currently maintained by brian d foy.
760              
761             =head1 LICENCE AND COPYRIGHT
762              
763             Copyright (c) 2007, Andy Armstrong C<< <andy@hexten.net> >>. All rights reserved.
764              
765             This module is free software; you can redistribute it and/or
766             modify it under the same terms as Perl itself. See L<perlartistic>.
767              
768             =head1 DISCLAIMER OF WARRANTY
769              
770             BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
771             FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
772             OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
773             PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
774             EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
775             WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
776             ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH
777             YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
778             NECESSARY SERVICING, REPAIR, OR CORRECTION.
779              
780             IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
781             WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
782             REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENCE, BE
783             LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL,
784             OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
785             THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
786             RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
787             FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
788             SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
789             SUCH DAMAGES.