File Coverage

blib/lib/version/vpp.pm
Criterion Covered Total %
statement 465 532 87.4
branch 195 276 70.6
condition 109 158 68.9
subroutine 48 49 97.9
pod 0 16 0.0
total 817 1031 79.2


line stmt bran cond sub pod time code
1             package charstar;
2             # a little helper class to emulate C char* semantics in Perl
3             # so that prescan_version can use the same code as in C
4              
5             use overload (
6 1         18 '""' => \&thischar,
7             '0+' => \&thischar,
8             '++' => \&increment,
9             '--' => \&decrement,
10             '+' => \&plus,
11             '-' => \&minus,
12             '*' => \&multiply,
13             'cmp' => \&cmp,
14             '<=>' => \&spaceship,
15             'bool' => \&thischar,
16             '=' => \&clone,
17 1     1   150127 );
  1         3  
18              
19             sub new {
20 1344     1344   2828 my ($self, $string) = @_;
21 1344   66     3528 my $class = ref($self) || $self;
22              
23 1344         6502 my $obj = {
24             string => [split(//,$string)],
25             current => 0,
26             };
27 1344         3862 return bless $obj, $class;
28             }
29              
30             sub thischar {
31 32140     32140   43837 my ($self) = @_;
32 32140         35558 my $last = $#{$self->{string}};
  32140         48773  
33 32140         43250 my $curr = $self->{current};
34 32140 100 66     83236 if ($curr >= 0 && $curr <= $last) {
35 24444         64483 return $self->{string}->[$curr];
36             }
37             else {
38 7696         18984 return '';
39             }
40             }
41              
42             sub increment {
43 9656     9656   14220 my ($self) = @_;
44 9656         16320 $self->{current}++;
45             }
46              
47             sub decrement {
48 2832     2832   4166 my ($self) = @_;
49 2832         5550 $self->{current}--;
50             }
51              
52             sub plus {
53 36     36   66 my ($self, $offset) = @_;
54 36         66 my $rself = $self->clone;
55 36         64 $rself->{current} += $offset;
56 36         76 return $rself;
57             }
58              
59             sub minus {
60 512     512   880 my ($self, $offset) = @_;
61 512         917 my $rself = $self->clone;
62 512         800 $rself->{current} -= $offset;
63 512         1139 return $rself;
64             }
65              
66             sub multiply {
67 1948     1948   3190 my ($left, $right, $swapped) = @_;
68 1948         2988 my $char = $left->thischar();
69 1948         4010 return $char * $right;
70             }
71              
72             sub spaceship {
73 4192     4192   7294 my ($left, $right, $swapped) = @_;
74 4192 50       7267 unless (ref($right)) { # not an object already
75 0         0 $right = $left->new($right);
76             }
77 4192         10966 return $left->{current} <=> $right->{current};
78             }
79              
80             sub cmp {
81 11856     11856   19495 my ($left, $right, $swapped) = @_;
82 11856 50       19930 unless (ref($right)) { # not an object already
83 11856 100       19924 if (length($right) == 1) { # comparing single character only
84 11276         18450 return $left->thischar cmp $right;
85             }
86 580         1151 $right = $left->new($right);
87             }
88 580         1071 return $left->currstr cmp $right->currstr;
89             }
90              
91             sub bool {
92 0     0   0 my ($self) = @_;
93 0         0 my $char = $self->thischar;
94 0         0 return ($char ne '');
95             }
96              
97             sub clone {
98 3888     3888   6720 my ($left, $right, $swapped) = @_;
99             $right = {
100 3888         15246 string => [@{$left->{string}}],
101             current => $left->{current},
102 3888         5014 };
103 3888         9461 return bless $right, ref($left);
104             }
105              
106             sub currstr {
107 1644     1644   2629 my ($self, $s) = @_;
108 1644         2308 my $curr = $self->{current};
109 1644         1831 my $last = $#{$self->{string}};
  1644         2656  
110 1644 50 66     4150 if (defined($s) && $s->{current} < $last) {
111 0         0 $last = $s->{current};
112             }
113              
114 1644         2819 my $string = join('', @{$self->{string}}[$curr..$last]);
  1644         3460  
115 1644         4945 return $string;
116             }
117              
118             package version::vpp;
119              
120 1     1   920 use 5.006002;
  1         5  
121 1     1   13 use strict;
  1         3  
  1         31  
122 1     1   7 use warnings::register;
  1         6  
  1         115  
123              
124 1     1   10 use Config;
  1         5  
  1         334  
125              
126             our $VERSION = 0.9928;
127             our $CLASS = 'version::vpp';
128             our ($LAX, $STRICT, $WARN_CATEGORY);
129              
130             if ($] > 5.015) {
131             warnings::register_categories(qw/version/);
132             $WARN_CATEGORY = 'version';
133             } else {
134             $WARN_CATEGORY = 'numeric';
135             }
136              
137             require version::regex;
138             *version::vpp::is_strict = \&version::regex::is_strict;
139             *version::vpp::is_lax = \&version::regex::is_lax;
140             *LAX = \$version::regex::LAX;
141             *STRICT = \$version::regex::STRICT;
142              
143             use overload (
144 1         8 '""' => \&stringify,
145             '0+' => \&numify,
146             'cmp' => \&vcmp,
147             '<=>' => \&vcmp,
148             'bool' => \&vbool,
149             '+' => \&vnoop,
150             '-' => \&vnoop,
151             '*' => \&vnoop,
152             '/' => \&vnoop,
153             '+=' => \&vnoop,
154             '-=' => \&vnoop,
155             '*=' => \&vnoop,
156             '/=' => \&vnoop,
157             'abs' => \&vnoop,
158 1     1   6 );
  1         2  
159              
160             sub import {
161 1     1   201 no strict 'refs';
  1         1  
  1         269  
162 9     9   117 my ($class) = shift;
163              
164             # Set up any derived class
165 9 100       40 unless ($class eq $CLASS) {
166 4         19 local $^W;
167 4         9 *{$class.'::declare'} = \&{$CLASS.'::declare'};
  4         26  
  4         21  
168 4         10 *{$class.'::qv'} = \&{$CLASS.'::qv'};
  4         21  
  4         15  
169             }
170              
171 9         21 my %args;
172 9 100       30 if (@_) { # any remaining terms are arguments
173 4         16 map { $args{$_} = 1 } @_
  8         23  
174             }
175             else { # no parameters at all on use line
176 5         20 %args =
177             (
178             qv => 1,
179             'UNIVERSAL::VERSION' => 1,
180             );
181             }
182              
183 9         27 my $callpkg = caller();
184              
185 9 100       29 if (exists($args{declare})) {
186 3         11 *{$callpkg.'::declare'} =
187 4     4   677 sub {return $class->declare(shift) }
188 4 100       7 unless defined(&{$callpkg.'::declare'});
  4         35  
189             }
190              
191 9 50       24 if (exists($args{qv})) {
192 7         31 *{$callpkg.'::qv'} =
193 4     4   676 sub {return $class->qv(shift) }
194 9 100       17 unless defined(&{$callpkg.'::qv'});
  9         70  
195             }
196              
197 9 100       28 if (exists($args{'UNIVERSAL::VERSION'})) {
198 1     1   7 no warnings qw/redefine/;
  1         2  
  1         206  
199             *UNIVERSAL::VERSION
200 5         9 = \&{$CLASS.'::_VERSION'};
  5         37  
201             }
202              
203 9 50       27 if (exists($args{'VERSION'})) {
204 0         0 *{$callpkg.'::VERSION'} = \&{$CLASS.'::_VERSION'};
  0         0  
  0         0  
205             }
206              
207 9 50       28 if (exists($args{'is_strict'})) {
208 0         0 *{$callpkg.'::is_strict'} = \&{$CLASS.'::is_strict'}
  0         0  
209 0 0       0 unless defined(&{$callpkg.'::is_strict'});
  0         0  
210             }
211              
212 9 50       276 if (exists($args{'is_lax'})) {
213 0         0 *{$callpkg.'::is_lax'} = \&{$CLASS.'::is_lax'}
  0         0  
214 0 0       0 unless defined(&{$callpkg.'::is_lax'});
  0         0  
215             }
216             }
217              
218             my $VERSION_MAX = 0x7FFFFFFF;
219              
220             # implement prescan_version as closely to the C version as possible
221 1     1   6 use constant TRUE => 1;
  1         2  
  1         107  
222 1     1   28 use constant FALSE => 0;
  1         3  
  1         2128  
223              
224             sub isDIGIT {
225 12100     12100 0 19734 my ($char) = shift->thischar();
226 12100         38997 return ($char =~ /\d/);
227             }
228              
229             sub isALPHA {
230 1016     1016 0 1703 my ($char) = shift->thischar();
231 1016         3055 return ($char =~ /[a-zA-Z]/);
232             }
233              
234             sub isSPACE {
235 1648     1648 0 3057 my ($char) = shift->thischar();
236 1648         4759 return ($char =~ /\s/);
237             }
238              
239             sub BADVERSION {
240 68     68 0 134 my ($s, $errstr, $error) = @_;
241 68 50       148 if ($errstr) {
242 68         104 $$errstr = $error;
243             }
244 68         287 return $s;
245             }
246              
247             sub prescan_version {
248 580     580 0 1326 my ($s, $strict, $errstr, $sqv, $ssaw_decimal, $swidth, $salpha) = @_;
249 580 50       1141 my $qv = defined $sqv ? $$sqv : FALSE;
250 580 50       992 my $saw_decimal = defined $ssaw_decimal ? $$ssaw_decimal : 0;
251 580 50       997 my $width = defined $swidth ? $$swidth : 3;
252 580 50       960 my $alpha = defined $salpha ? $$salpha : FALSE;
253              
254 580         783 my $d = $s;
255              
256 580 100 66     1173 if ($qv && isDIGIT($d)) {
257 8         186 goto dotted_decimal_version;
258             }
259              
260 580 100       1386 if ($d eq 'v') { # explicit v-string
261 152         323 $d++;
262 152 100       282 if (isDIGIT($d)) {
263 132         186 $qv = TRUE;
264             }
265             else { # degenerate v-string
266             # requires v1.2.3
267 20         93 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
268             }
269              
270             dotted_decimal_version:
271 332 0 33     921 if ($strict && $d eq '0' && isDIGIT($d+1)) {
      33        
272             # no leading zeros allowed
273 0         0 return BADVERSION($s,$errstr,"Invalid version format (no leading zeros)");
274             }
275              
276 332         625 while (isDIGIT($d)) { # integer part
277 348         735 $d++;
278             }
279              
280 332 100       684 if ($d eq '.')
281             {
282 328         452 $saw_decimal++;
283 328         559 $d++; # decimal point
284             }
285             else
286             {
287 4 50       18 if ($strict) {
288             # require v1.2.3
289 0         0 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
290             }
291             else {
292 4         117 goto version_prescan_finish;
293             }
294             }
295              
296             {
297 328         456 my $i = 0;
  328         446  
298 328         412 my $j = 0;
299 328         511 while (isDIGIT($d)) { # just keep reading
300 708         1073 $i++;
301 708         1216 while (isDIGIT($d)) {
302 920         1717 $d++; $j++;
  920         1172  
303             # maximum 3 digits between decimal
304 920 50 33     1911 if ($strict && $j > 3) {
305 0         0 return BADVERSION($s,$errstr,"Invalid version format (maximum 3 digits between decimals)");
306             }
307             }
308 708 100       1402 if ($d eq '_') {
    100          
    50          
309 52 50       123 if ($strict) {
310 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
311             }
312 52 50       86 if ( $alpha ) {
313 0         0 return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
314             }
315 52         86 $d++;
316 52         72 $alpha = TRUE;
317             }
318             elsif ($d eq '.') {
319 336 50       646 if ($alpha) {
320 0         0 return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
321             }
322 336         407 $saw_decimal++;
323 336         509 $d++;
324             }
325             elsif (!isDIGIT($d)) {
326 320         463 last;
327             }
328 388         691 $j = 0;
329             }
330              
331 328 50 33     777 if ($strict && $i < 2) {
332             # requires v1.2.3
333 0         0 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
334             }
335             }
336             } # end if dotted-decimal
337             else
338             { # decimal versions
339 420         675 my $j = 0;
340             # special $strict case for leading '.' or '0'
341 420 50       795 if ($strict) {
342 0 0       0 if ($d eq '.') {
343 0         0 return BADVERSION($s,$errstr,"Invalid version format (0 before decimal required)");
344             }
345 0 0 0     0 if ($d eq '0' && isDIGIT($d+1)) {
346 0         0 return BADVERSION($s,$errstr,"Invalid version format (no leading zeros)");
347             }
348             }
349              
350             # and we never support negative version numbers
351 420 100       788 if ($d eq '-') {
352 4         21 return BADVERSION($s,$errstr,"Invalid version format (negative version number)");
353             }
354              
355             # consume all of the integer part
356 416         855 while (isDIGIT($d)) {
357 1776         3370 $d++;
358             }
359              
360             # look for a fractional part
361 416 100 66     886 if ($d eq '.') {
    100 100        
    100 66        
    100          
    50          
362             # we found it, so consume it
363 328         465 $saw_decimal++;
364 328         554 $d++;
365             }
366             elsif (!$d || $d eq ';' || isSPACE($d) || $d eq '}') {
367 72 50       150 if ( $d == $s ) {
368             # found nothing
369 0         0 return BADVERSION($s,$errstr,"Invalid version format (version required)");
370             }
371             # found just an integer
372 72         1901 goto version_prescan_finish;
373             }
374             elsif ( $d == $s ) {
375             # didn't find either integer or period
376 4         14 return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
377             }
378             elsif ($d eq '_') {
379             # underscore can't come after integer part
380 4 50       22 if ($strict) {
    50          
381 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
382             }
383             elsif (isDIGIT($d+1)) {
384 4         13 return BADVERSION($s,$errstr,"Invalid version format (alpha without decimal)");
385             }
386             else {
387 0         0 return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)");
388             }
389             }
390             elsif ($d) {
391             # anything else after integer part is just invalid data
392 8         31 return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
393             }
394              
395             # scan the fractional part after the decimal point
396 328 50 100     688 if ($d && !isDIGIT($d) && ($strict || ! ($d eq ';' || isSPACE($d) || $d eq '}') )) {
      33        
      66        
397             # $strict or lax-but-not-the-end
398 4         13 return BADVERSION($s,$errstr,"Invalid version format (fractional part required)");
399             }
400              
401 324         697 while (isDIGIT($d)) {
402 588         1117 $d++; $j++;
  588         785  
403 588 100 66     958 if ($d eq '.' && isDIGIT($d-1)) {
404 196 100       349 if ($alpha) {
405 4         15 return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
406             }
407 192 50       341 if ($strict) {
408 0         0 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions must begin with 'v')");
409             }
410 192         304 $d = $s; # start all over again
411 192         519 $qv = TRUE;
412 192         1454 goto dotted_decimal_version;
413             }
414 392 100       691 if ($d eq '_') {
415 32 50       69 if ($strict) {
416 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
417             }
418 32 100       60 if ( $alpha ) {
419 4         10 return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
420             }
421 28 50       63 if ( ! isDIGIT($d+1) ) {
422 0         0 return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)");
423             }
424 28         76 $width = $j;
425 28         52 $d++;
426 28         54 $alpha = TRUE;
427             }
428             }
429             }
430              
431             version_prescan_finish:
432 528         1125 while (isSPACE($d)) {
433 4         24 $d++;
434             }
435              
436 528 50 66     1063 if ($d && !isDIGIT($d) && (! ($d eq ';' || $d eq '}') )) {
      33        
      66        
437             # trailing non-numeric data
438 8         26 return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
439             }
440 520 100 100     1315 if ($saw_decimal > 1 && ($d-1) eq '.') {
441             # no trailing period allowed
442 8         25 return BADVERSION($s,$errstr,"Invalid version format (trailing decimal)");
443             }
444              
445 512 50       1250 if (defined $sqv) {
446 512         822 $$sqv = $qv;
447             }
448 512 50       839 if (defined $swidth) {
449 512         706 $$swidth = $width;
450             }
451 512 50       828 if (defined $ssaw_decimal) {
452 512         671 $$ssaw_decimal = $saw_decimal;
453             }
454 512 50       884 if (defined $salpha) {
455 512         652 $$salpha = $alpha;
456             }
457 512         1044 return $d;
458             }
459              
460             sub scan_version {
461 580     580 0 1106 my ($s, $rv, $qv) = @_;
462 580         1682 my $start;
463             my $pos;
464 580         0 my $last;
465 580         0 my $errstr;
466 580         786 my $saw_decimal = 0;
467 580         690 my $width = 3;
468 580         702 my $alpha = FALSE;
469 580         682 my $vinf = FALSE;
470 580         750 my @av;
471              
472 580         1405 $s = new charstar $s;
473              
474 580         1241 while (isSPACE($s)) { # leading whitespace is OK
475 4         13 $s++;
476             }
477              
478 580         1488 $last = prescan_version($s, FALSE, \$errstr, \$qv, \$saw_decimal,
479             \$width, \$alpha);
480              
481 580 100       1269 if ($errstr) {
482             # 'undef' is a special case and not an error
483 68 50       149 if ( $s ne 'undef') {
484 68         392 require Carp;
485 68         7092 Carp::croak($errstr);
486             }
487             }
488              
489 512         641 $start = $s;
490 512 100       909 if ($s eq 'v') {
491 132         250 $s++;
492             }
493 512         764 $pos = $s;
494              
495 512 100       934 if ( $qv ) {
496 324         686 $$rv->{qv} = $qv;
497             }
498 512 100       1002 if ( $alpha ) {
499 72         126 $$rv->{alpha} = $alpha;
500             }
501 512 100 100     1512 if ( !$qv && $width < 3 ) {
502 16         43 $$rv->{width} = $width;
503             }
504              
505 512   66     915 while (isDIGIT($pos) || $pos eq '_') {
506 1880         3744 $pos++;
507             }
508 512 50       1139 if (!isALPHA($pos)) {
509 512         687 my $rev;
510              
511 512         621 for (;;) {
512 1292         1724 $rev = 0;
513             {
514             # this is atoi() that delimits on underscores
515 1292         1611 my $end = $pos;
  1292         1645  
516 1292         1662 my $mult = 1;
517 1292         1680 my $orev;
518              
519             # the following if() will only be true after the decimal
520             # point of a version originally created with a bare
521             # floating point number, i.e. not quoted in any way
522             #
523 1292 100 100     3041 if ( !$qv && $s > $start && $saw_decimal == 1 ) {
      66        
524 140         206 $mult *= 100;
525 140         237 while ( $s < $end ) {
526 320 50       560 next if $s eq '_';
527 320         478 $orev = $rev;
528 320         530 $rev += $s * $mult;
529 320         490 $mult /= 10;
530 320 50 33     1022 if ( (abs($orev) > abs($rev))
531             || (abs($rev) > $VERSION_MAX )) {
532 0         0 warn("Integer overflow in version %d",
533             $VERSION_MAX);
534 0         0 $s = $end - 1;
535 0         0 $rev = $VERSION_MAX;
536 0         0 $vinf = 1;
537             }
538 320         523 $s++;
539 320 100       533 if ( $s eq '_' ) {
540 20         41 $s++;
541             }
542             }
543             }
544             else {
545 1152         2198 while (--$end >= $s) {
546 1680 100       2865 next if $end eq '_';
547 1628         2373 $orev = $rev;
548 1628         2750 $rev += $end * $mult;
549 1628         2238 $mult *= 10;
550 1628 100 66     5999 if ( (abs($orev) > abs($rev))
551             || (abs($rev) > $VERSION_MAX )) {
552 28         357 warn("Integer overflow in version");
553 28         192 $end = $s - 1;
554 28         131 $rev = $VERSION_MAX;
555 28         68 $vinf = 1;
556             }
557             }
558             }
559             }
560              
561             # Append revision
562 1292         2340 push @av, $rev;
563 1292 100 66     2610 if ( $vinf ) {
    100 33        
    100          
    50          
    100          
564 28         40 $s = $last;
565 28         61 last;
566             }
567             elsif ( $pos eq '.' ) {
568 748         1325 $s = ++$pos;
569             }
570             elsif ( $pos eq '_' && isDIGIT($pos+1) ) {
571 4         9 $s = ++$pos;
572             }
573             elsif ( $pos eq ',' && isDIGIT($pos+1) ) {
574 0         0 $s = ++$pos;
575             }
576             elsif ( isDIGIT($pos) ) {
577 28         57 $s = $pos;
578             }
579             else {
580 484         706 $s = $pos;
581 484         1248 last;
582             }
583 780 100       1761 if ( $qv ) {
584 640   100     1174 while ( isDIGIT($pos) || $pos eq '_') {
585 920         1824 $pos++;
586             }
587             }
588             else {
589 140         201 my $digits = 0;
590 140   100     246 while ( ( isDIGIT($pos) || $pos eq '_' ) && $digits < 3 ) {
      100        
591 336 100       687 if ( $pos ne '_' ) {
592 320         440 $digits++;
593             }
594 336         582 $pos++;
595             }
596             }
597             }
598             }
599 512 100       1102 if ( $qv ) { # quoted versions always get at least three terms
600 324         478 my $len = $#av;
601             # This for loop appears to trigger a compiler bug on OS X, as it
602             # loops infinitely. Yes, len is negative. No, it makes no sense.
603             # Compiler in question is:
604             # gcc version 3.3 20030304 (Apple Computer, Inc. build 1640)
605             # for ( len = 2 - len; len > 0; len-- )
606             # av_push(MUTABLE_AV(sv), newSViv(0));
607             #
608 324         457 $len = 2 - $len;
609 324         640 while ($len-- > 0) {
610 52         108 push @av, 0;
611             }
612             }
613              
614             # need to save off the current version string for later
615 512 100       1077 if ( $vinf ) {
    50          
616 28         74 $$rv->{original} = "v.Inf";
617 28         55 $$rv->{vinf} = 1;
618             }
619             elsif ( $s > $start ) {
620 484         957 $$rv->{original} = $start->currstr($s);
621 484 100 100     1693 if ( $qv && $saw_decimal == 1 && $start ne 'v' ) {
      100        
622             # need to insert a v to be consistent
623 4         26 $$rv->{original} = 'v' . $$rv->{original};
624             }
625             }
626             else {
627 0         0 $$rv->{original} = '0';
628 0         0 push(@av, 0);
629             }
630              
631             # And finally, store the AV in the hash
632 512         1023 $$rv->{version} = \@av;
633              
634             # fix RT#19517 - special case 'undef' as string
635 512 50       892 if ($s eq 'undef') {
636 0         0 $s += 5;
637             }
638              
639 512         1883 return $s;
640             }
641              
642             sub new {
643 620     620 0 95567 my $class = shift;
644 620 50 33     1801 unless (defined $class or $#_ > 1) {
645 0         0 require Carp;
646 0         0 Carp::croak('Usage: version::new(class, version)');
647             }
648              
649 620   66     2513 my $self = bless ({}, ref ($class) || $class);
650 620         973 my $qv = FALSE;
651              
652 620 100       1339 if ( $#_ == 1 ) { # must be CVS-style
653 8         21 $qv = TRUE;
654             }
655 620         1076 my $value = pop; # always going to be the last element
656              
657 620 50 66     3809 if ( ref($value) && eval('$value->isa("version")') ) {
658             # Can copy the elements directly
659 0         0 $self->{version} = [ @{$value->{version} } ];
  0         0  
660 0 0       0 $self->{qv} = 1 if $value->{qv};
661 0 0       0 $self->{alpha} = 1 if $value->{alpha};
662 0         0 $self->{original} = ''.$value->{original};
663 0         0 return $self;
664             }
665              
666 620 100 100     2858 if ( not defined $value or $value =~ /^undef$/ ) {
667             # RT #19517 - special case for undef comparison
668             # or someone forgot to pass a value
669 32         54 push @{$self->{version}}, 0;
  32         276  
670 32         99 $self->{original} = "0";
671 32         150 return ($self);
672             }
673              
674              
675 588 100       1273 if (ref($value) =~ m/ARRAY|HASH/) {
676 8         41 require Carp;
677 8         725 Carp::croak("Invalid version format (non-numeric data)");
678             }
679              
680 580         1206 $value = _un_vstring($value);
681              
682 580 50       4238 if ($Config{d_setlocale}) {
683 1     1   9 use POSIX qw/locale_h/;
  1         2  
  1         6  
684 1     1   2477 use if $Config{d_setlocale}, 'locale';
  1         16  
  1         8  
685 580         2304 my $currlocale = setlocale(LC_ALL);
686              
687             # if the current locale uses commas for decimal points, we
688             # just replace commas with decimal places, rather than changing
689             # locales
690 580 50       4280 if ( localeconv()->{decimal_point} eq ',' ) {
691 0         0 $value =~ tr/,/./;
692             }
693             }
694              
695             # exponential notation
696 580 100       2459 if ( $value =~ /\d+.?\d*e[-+]?\d+/ ) {
697 20         276 $value = sprintf("%.9f",$value);
698 20         146 $value =~ s/(0+)$//; # trim trailing zeros
699             }
700              
701 580         1318 my $s = scan_version($value, \$self, $qv);
702              
703 512 50       1106 if ($s) { # must be something left over
704 0         0 warn(sprintf "Version string '%s' contains invalid data; "
705             ."ignoring: '%s'", $value, $s);
706             }
707              
708 512         2427 return ($self);
709             }
710              
711             *parse = \&new;
712              
713             sub numify {
714 52     52 0 285 my ($self) = @_;
715 52 50       114 unless (_verify($self)) {
716 0         0 require Carp;
717 0         0 Carp::croak("Invalid version object");
718             }
719 52   100     180 my $alpha = $self->{alpha} || "";
720 52         69 my $len = $#{$self->{version}};
  52         104  
721 52         84 my $digit = $self->{version}[0];
722 52         205 my $string = sprintf("%d.", $digit );
723              
724 52 100 66     690 if ($alpha and warnings::enabled()) {
725 8         933 warnings::warn($WARN_CATEGORY, 'alpha->numify() is lossy');
726             }
727              
728 52         443 for ( my $i = 1 ; $i <= $len ; $i++ ) {
729 88         144 $digit = $self->{version}[$i];
730 88         244 $string .= sprintf("%03d", $digit);
731             }
732              
733 52 100       110 if ( $len == 0 ) {
734 4         10 $string .= sprintf("000");
735             }
736              
737 52         343 return $string;
738             }
739              
740             sub normal {
741 24     24 0 83 my ($self) = @_;
742 24 50       48 unless (_verify($self)) {
743 0         0 require Carp;
744 0         0 Carp::croak("Invalid version object");
745             }
746              
747 24         44 my $len = $#{$self->{version}};
  24         48  
748 24         46 my $digit = $self->{version}[0];
749 24         77 my $string = sprintf("v%d", $digit );
750              
751 24         65 for ( my $i = 1 ; $i <= $len ; $i++ ) {
752 36         60 $digit = $self->{version}[$i];
753 36         91 $string .= sprintf(".%d", $digit);
754             }
755              
756 24 50       56 if ( $len <= 2 ) {
757 24         68 for ( $len = 2 - $len; $len != 0; $len-- ) {
758 12         33 $string .= sprintf(".%0d", 0);
759             }
760             }
761              
762 24         996 return $string;
763             }
764              
765             sub stringify {
766 328     328 0 1143 my ($self) = @_;
767 328 50       574 unless (_verify($self)) {
768 0         0 require Carp;
769 0         0 Carp::croak("Invalid version object");
770             }
771             return exists $self->{original}
772             ? $self->{original}
773             : exists $self->{qv}
774 328 0       5382 ? $self->normal
    50          
775             : $self->numify;
776             }
777              
778             sub vcmp {
779 260     260 0 20684 my ($left,$right,$swap) = @_;
780 260 50       618 die "Usage: version::vcmp(lobj, robj, ...)" if @_ < 2;
781 260         461 my $class = ref($left);
782 260 100       1024 unless ( UNIVERSAL::isa($right, $class) ) {
783 104         277 $right = $class->new($right);
784             }
785              
786 256 100       551 if ( $swap ) {
787 20         50 ($left, $right) = ($right, $left);
788             }
789 256 50       476 unless (_verify($left)) {
790 0         0 require Carp;
791 0         0 Carp::croak("Invalid version object");
792             }
793 256 50       439 unless (_verify($right)) {
794 0         0 require Carp;
795 0         0 Carp::croak("Invalid version format");
796             }
797 256         321 my $l = $#{$left->{version}};
  256         438  
798 256         337 my $r = $#{$right->{version}};
  256         366  
799 256 100       461 my $m = $l < $r ? $l : $r;
800 256         565 my $lalpha = $left->is_alpha;
801 256         421 my $ralpha = $right->is_alpha;
802 256         346 my $retval = 0;
803 256         312 my $i = 0;
804 256   100     833 while ( $i <= $m && $retval == 0 ) {
805 612         944 $retval = $left->{version}[$i] <=> $right->{version}[$i];
806 612         1498 $i++;
807             }
808              
809             # possible match except for trailing 0's
810 256 100 100     754 if ( $retval == 0 && $l != $r ) {
811 40 100       111 if ( $l < $r ) {
812 24   66     122 while ( $i <= $r && $retval == 0 ) {
813 24 100       58 if ( $right->{version}[$i] != 0 ) {
814 20         34 $retval = -1; # not a match after all
815             }
816 24         52 $i++;
817             }
818             }
819             else {
820 16   100     107 while ( $i <= $l && $retval == 0 ) {
821 20 100       71 if ( $left->{version}[$i] != 0 ) {
822 12         25 $retval = +1; # not a match after all
823             }
824 20         53 $i++;
825             }
826             }
827             }
828              
829 256         1930 return $retval;
830             }
831              
832             sub vbool {
833 8     8 0 880 my ($self) = @_;
834 8         37 return vcmp($self,$self->new("0"),1);
835             }
836              
837             sub vnoop {
838 28     28 0 6314 require Carp;
839 28         2246 Carp::croak("operation not supported with version object");
840             }
841              
842             sub is_alpha {
843 524     524 0 797 my ($self) = @_;
844 524         1012 return (exists $self->{alpha});
845             }
846              
847             sub qv {
848 24     24 0 1401 my $value = shift;
849 24         45 my $class = $CLASS;
850 24 50       90 if (@_) {
851 24   33     130 $class = ref($value) || $value;
852 24         55 $value = shift;
853             }
854              
855 24         64 $value = _un_vstring($value);
856 24 100       159 $value = 'v'.$value unless $value =~ /(^v|\d+\.\d+\.\d)/;
857 24         85 my $obj = $CLASS->new($value);
858 24         188 return bless $obj, $class;
859             }
860              
861             *declare = \&qv;
862              
863             sub is_qv {
864 36     36 0 80 my ($self) = @_;
865 36         92 return (exists $self->{qv});
866             }
867              
868              
869             sub _verify {
870 916     916   1454 my ($self) = @_;
871 916 50 33     2064 if ( ref($self)
      33        
872 916         4062 && eval { exists $self->{version} }
873             && ref($self->{version}) eq 'ARRAY'
874             ) {
875 916         2253 return 1;
876             }
877             else {
878 0         0 return 0;
879             }
880             }
881              
882             sub _is_non_alphanumeric {
883 184     184   331 my $s = shift;
884 184         492 $s = new charstar $s;
885 184         499 while ($s) {
886 508 100       964 return 0 if isSPACE($s); # early out
887 504 100 100     925 return 1 unless (isALPHA($s) || isDIGIT($s) || $s =~ /[.-]/);
      100        
888 432         875 $s++;
889             }
890 108         466 return 0;
891             }
892              
893             sub _un_vstring {
894 604     604   960 my $value = shift;
895             # may be a v-string
896 604 100 66     3477 if ( length($value) >= 1 && $value !~ /[,._]/
      100        
897             && _is_non_alphanumeric($value)) {
898 72         116 my $tvalue;
899 72 50       165 if ( $] >= 5.008_001 ) {
    0          
900 72         154 $tvalue = _find_magic_vstring($value);
901 72 100       198 $value = $tvalue if length $tvalue;
902             }
903             elsif ( $] >= 5.006_000 ) {
904 0         0 $tvalue = sprintf("v%vd",$value);
905 0 0       0 if ( $tvalue =~ /^v\d+(\.\d+)*$/ ) {
906             # must be a v-string
907 0         0 $value = $tvalue;
908             }
909             }
910             }
911 604         1203 return $value;
912             }
913              
914             sub _find_magic_vstring {
915 72     72   140 my $value = shift;
916 72         106 my $tvalue = '';
917 72         364 require B;
918 72         296 my $sv = B::svref_2object(\$value);
919 72 50       337 my $magic = ref($sv) eq 'B::PVMG' ? $sv->MAGIC : undef;
920 72         184 while ( $magic ) {
921 60 50       195 if ( $magic->TYPE eq 'V' ) {
922 60         141 $tvalue = $magic->PTR;
923 60         487 $tvalue =~ s/^v?(.+)$/v$1/;
924 60         120 last;
925             }
926             else {
927 0         0 $magic = $magic->MOREMAGIC;
928             }
929             }
930 72         212 $tvalue =~ tr/_//d;
931 72         217 return $tvalue;
932             }
933              
934             sub _VERSION {
935 96     96   51810 my ($obj, $req) = @_;
936 96   33     414 my $class = ref($obj) || $obj;
937              
938 1     1   3133 no strict 'refs';
  1         2  
  1         355  
939 96 100 100     339 if ( exists $INC{"$class.pm"} and not %{"$class\::"} and $] >= 5.008) {
  84   66     351  
940             # file but no package
941 4         25 require Carp;
942 4         428 Carp::croak( "$class defines neither package nor VERSION"
943             ."--version check failed");
944             }
945              
946 92         4811 my $version = eval "\$$class\::VERSION";
947 92 100       429 if ( defined $version ) {
948 68 50       170 local $^W if $] <= 5.008;
949 68         234 $version = version::vpp->new($version);
950             }
951              
952 84 100       239 if ( defined $req ) {
953 60 100       137 unless ( defined $version ) {
954 8         44 require Carp;
955 8 50       57 my $msg = $] < 5.006
956             ? "$class version $req required--this is only version "
957             : "$class does not define \$$class\::VERSION"
958             ."--version check failed";
959              
960 8 50       26 if ( $ENV{VERSION_DEBUG} ) {
961 0         0 Carp::confess($msg);
962             }
963             else {
964 8         799 Carp::croak($msg);
965             }
966             }
967              
968 52         124 $req = version::vpp->new($req);
969              
970 52 100       131 if ( $req > $version ) {
971 36         214 require Carp;
972 36 100       118 if ( $req->is_qv ) {
973 8         29 Carp::croak(
974             sprintf ("%s version %s required--".
975             "this is only version %s", $class,
976             $req->normal, $version->normal)
977             );
978             }
979             else {
980 28         76 Carp::croak(
981             sprintf ("%s version %s required--".
982             "this is only version %s", $class,
983             $req->stringify, $version->stringify)
984             );
985             }
986             }
987             }
988              
989 40 100       297 return defined $version ? $version->stringify : undef;
990             }
991              
992             1; #this line is important and will help the module return a true value