File Coverage

blib/lib/version/vpp.pm
Criterion Covered Total %
statement 476 543 87.6
branch 199 278 71.5
condition 110 161 68.3
subroutine 52 53 98.1
pod 0 20 0.0
total 837 1055 79.3


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         21 '""' => \&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   225334 );
  1         3  
18              
19             sub new {
20 1418     1418   2985 my ($self, $string) = @_;
21 1418   66     3645 my $class = ref($self) || $self;
22              
23 1418         8620 my $obj = {
24             string => [split(//,$string)],
25             current => 0,
26             };
27 1418         4847 return bless $obj, $class;
28             }
29              
30             sub thischar {
31 34643     34643   47313 my ($self) = @_;
32 34643         39009 my $last = $#{$self->{string}};
  34643         49736  
33 34643         46119 my $curr = $self->{current};
34 34643 100 66     88941 if ($curr >= 0 && $curr <= $last) {
35 26392         68068 return $self->{string}->[$curr];
36             }
37             else {
38 8251         21384 return '';
39             }
40             }
41              
42             sub increment {
43 10273     10273   15305 my ($self) = @_;
44 10273         17032 $self->{current}++;
45             }
46              
47             sub decrement {
48 2962     2962   4494 my ($self) = @_;
49 2962         6035 $self->{current}--;
50             }
51              
52             sub plus {
53 36     36   78 my ($self, $offset) = @_;
54 36         87 my $rself = $self->clone;
55 36         65 $rself->{current} += $offset;
56 36         82 return $rself;
57             }
58              
59             sub minus {
60 524     524   1080 my ($self, $offset) = @_;
61 524         1152 my $rself = $self->clone;
62 524         809 $rself->{current} -= $offset;
63 524         1219 return $rself;
64             }
65              
66             sub multiply {
67 2132     2132   3387 my ($left, $right, $swapped) = @_;
68 2132         3520 my $char = $left->thischar();
69 2132         4215 return $char * $right;
70             }
71              
72             sub spaceship {
73 4589     4589   7268 my ($left, $right, $swapped) = @_;
74 4589 50       7735 unless (ref($right)) { # not an object already
75 0         0 $right = $left->new($right);
76             }
77 4589         13549 return $left->{current} <=> $right->{current};
78             }
79              
80             sub cmp {
81 13073     13073   21762 my ($left, $right, $swapped) = @_;
82 13073 50       20924 unless (ref($right)) { # not an object already
83 13073 100       20854 if (length($right) == 1) { # comparing single character only
84 12456         18917 return $left->thischar cmp $right;
85             }
86 617         1397 $right = $left->new($right);
87             }
88 617         1192 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 4122     4122   7278 my ($left, $right, $swapped) = @_;
99             $right = {
100 4122         19717 string => [@{$left->{string}}],
101             current => $left->{current},
102 4122         5004 };
103 4122         11270 return bless $right, ref($left);
104             }
105              
106             sub currstr {
107 1755     1755   2747 my ($self, $s) = @_;
108 1755         2338 my $curr = $self->{current};
109 1755         2029 my $last = $#{$self->{string}};
  1755         2635  
110 1755 50 66     4177 if (defined($s) && $s->{current} < $last) {
111 0         0 $last = $s->{current};
112             }
113              
114 1755         2799 my $string = join('', @{$self->{string}}[$curr..$last]);
  1755         4464  
115 1755         5902 return $string;
116             }
117              
118             package version::vpp;
119              
120 1     1   1305 use 5.006002;
  1         5  
121 1     1   9 use strict;
  1         2  
  1         33  
122 1     1   6 use warnings::register;
  1         3  
  1         77  
123              
124 1     1   6 use Config;
  1         2  
  1         382  
125              
126             our $VERSION = '0.9934';
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         12 '""' => \&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   9 );
  1         2  
159              
160             sub import {
161 1     1   308 no strict 'refs';
  1         2  
  1         375  
162 9     9   117 my ($class) = shift;
163              
164             # Set up any derived class
165 9 100       45 unless ($class eq $CLASS) {
166 4         20 local $^W;
167 4         7 *{$class.'::declare'} = \&{$CLASS.'::declare'};
  4         23  
  4         23  
168 4         7 *{$class.'::qv'} = \&{$CLASS.'::qv'};
  4         23  
  4         33  
169             }
170              
171 9         17 my %args;
172 9 100       36 if (@_) { # any remaining terms are arguments
173 4         10 map { $args{$_} = 1 } @_
  8         26  
174             }
175             else { # no parameters at all on use line
176 5         21 %args =
177             (
178             qv => 1,
179             'UNIVERSAL::VERSION' => 1,
180             );
181             }
182              
183 9         32 my $callpkg = caller();
184              
185 9 100       56 if (exists($args{declare})) {
186 3         10 *{$callpkg.'::declare'} =
187 4     4   1043 sub {return $class->declare(shift) }
188 4 100       7 unless defined(&{$callpkg.'::declare'});
  4         30  
189             }
190              
191 9 50       33 if (exists($args{qv})) {
192 7         27 *{$callpkg.'::qv'} =
193 4     4   745 sub {return $class->qv(shift) }
194 9 100       16 unless defined(&{$callpkg.'::qv'});
  9         72  
195             }
196              
197 9 100       31 if (exists($args{'UNIVERSAL::VERSION'})) {
198 1     1   11 no warnings qw/redefine/;
  1         3  
  1         351  
199             *UNIVERSAL::VERSION
200 5         14 = \&{$CLASS.'::_VERSION'};
  5         23  
201             }
202              
203 9 50       26 if (exists($args{'VERSION'})) {
204 0         0 *{$callpkg.'::VERSION'} = \&{$CLASS.'::_VERSION'};
  0         0  
  0         0  
205             }
206              
207 9 50       29 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       375 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   9 use constant TRUE => 1;
  1         1  
  1         145  
222 1     1   7 use constant FALSE => 0;
  1         3  
  1         3036  
223              
224             sub isDIGIT {
225 12930     12930 0 20930 my ($char) = shift->thischar();
226 12930         38231 return ($char =~ /\d/);
227             }
228              
229             sub isALPHA {
230 1053     1053 0 2190 my ($char) = shift->thischar();
231 1053         3043 return ($char =~ /[a-zA-Z]/);
232             }
233              
234             sub isSPACE {
235 1722     1722 0 3370 my ($char) = shift->thischar();
236 1722         4959 return ($char =~ /\s/);
237             }
238              
239             sub BADVERSION {
240 68     68 0 134 my ($s, $errstr, $error) = @_;
241 68 50       152 if ($errstr) {
242 68         116 $$errstr = $error;
243             }
244 68         296 return $s;
245             }
246              
247             sub prescan_version {
248 617     617 0 1395 my ($s, $strict, $errstr, $sqv, $ssaw_decimal, $swidth, $salpha) = @_;
249 617 50       1178 my $qv = defined $sqv ? $$sqv : FALSE;
250 617 50       961 my $saw_decimal = defined $ssaw_decimal ? $$ssaw_decimal : 0;
251 617 50       883 my $width = defined $swidth ? $$swidth : 3;
252 617 50       1301 my $alpha = defined $salpha ? $$salpha : FALSE;
253              
254 617         764 my $d = $s;
255              
256 617 100       1772 if ($d eq 'v') { # explicit v-string
257 164         343 $d++;
258 164 100       363 if (isDIGIT($d)) {
259 144         304 $qv = TRUE;
260             }
261             else { # degenerate v-string
262             # requires v1.2.3
263 20         58 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
264             }
265             }
266              
267 597 100 66     1654 if ($qv && isDIGIT($d)) {
268             dotted_decimal_version:
269 344 0 33     1131 if ($strict && $d eq '0' && isDIGIT($d+1)) {
      33        
270             # no leading zeros allowed
271 0         0 return BADVERSION($s,$errstr,"Invalid version format (no leading zeros)");
272             }
273              
274 344         647 while (isDIGIT($d)) { # integer part
275 360         800 $d++;
276             }
277              
278 344 100       720 if ($d eq '.')
279             {
280 340         454 $saw_decimal++;
281 340         565 $d++; # decimal point
282             }
283             else
284             {
285 4 50       12 if ($strict) {
286             # require v1.2.3
287 0         0 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
288             }
289             else {
290 4         171 goto version_prescan_finish;
291             }
292             }
293              
294             {
295 340         458 my $i = 0;
  340         459  
296 340         438 my $j = 0;
297 340         531 while (isDIGIT($d)) { # just keep reading
298 736         1004 $i++;
299 736         1206 while (isDIGIT($d)) {
300 948         1602 $d++; $j++;
  948         1195  
301             # maximum 3 digits between decimal
302 948 50 33     2192 if ($strict && $j > 3) {
303 0         0 return BADVERSION($s,$errstr,"Invalid version format (maximum 3 digits between decimals)");
304             }
305             }
306 736 100       1420 if ($d eq '_') {
    100          
    50          
307 52 50       128 if ($strict) {
308 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
309             }
310 52 50       116 if ( $alpha ) {
311 0         0 return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
312             }
313 52         93 $d++;
314 52         77 $alpha = TRUE;
315             }
316             elsif ($d eq '.') {
317 352 50       659 if ($alpha) {
318 0         0 return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
319             }
320 352         444 $saw_decimal++;
321 352         534 $d++;
322             }
323             elsif (!isDIGIT($d)) {
324 332         567 last;
325             }
326 404         721 $j = 0;
327             }
328              
329 340 50 33     953 if ($strict && $i < 2) {
330             # requires v1.2.3
331 0         0 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
332             }
333             }
334             } # end if dotted-decimal
335             else
336             { # decimal versions
337 445         1768 my $j = 0;
338             # special $strict case for leading '.' or '0'
339 445 50       789 if ($strict) {
340 0 0       0 if ($d eq '.') {
341 0         0 return BADVERSION($s,$errstr,"Invalid version format (0 before decimal required)");
342             }
343 0 0 0     0 if ($d eq '0' && isDIGIT($d+1)) {
344 0         0 return BADVERSION($s,$errstr,"Invalid version format (no leading zeros)");
345             }
346             }
347              
348             # and we never support negative version numbers
349 445 100       719 if ($d eq '-') {
350 4         10 return BADVERSION($s,$errstr,"Invalid version format (negative version number)");
351             }
352              
353             # consume all of the integer part
354 441         862 while (isDIGIT($d)) {
355 1801         3036 $d++;
356             }
357              
358             # look for a fractional part
359 441 100 66     918 if ($d eq '.') {
    100 100        
    100 66        
    100          
    50          
360             # we found it, so consume it
361 353         629 $saw_decimal++;
362 353         671 $d++;
363             }
364             elsif (!$d || $d eq ';' || isSPACE($d) || $d eq '}') {
365 72 50       160 if ( $d == $s ) {
366             # found nothing
367 0         0 return BADVERSION($s,$errstr,"Invalid version format (version required)");
368             }
369             # found just an integer
370 72         2734 goto version_prescan_finish;
371             }
372             elsif ( $d == $s ) {
373             # didn't find either integer or period
374 4         11 return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
375             }
376             elsif ($d eq '_') {
377             # underscore can't come after integer part
378 4 50       15 if ($strict) {
    50          
379 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
380             }
381             elsif (isDIGIT($d+1)) {
382 4         14 return BADVERSION($s,$errstr,"Invalid version format (alpha without decimal)");
383             }
384             else {
385 0         0 return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)");
386             }
387             }
388             elsif ($d) {
389             # anything else after integer part is just invalid data
390 8         20 return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
391             }
392              
393             # scan the fractional part after the decimal point
394 353 50 100     607 if ($d && !isDIGIT($d) && ($strict || ! ($d eq ';' || isSPACE($d) || $d eq '}') )) {
      33        
      66        
395             # $strict or lax-but-not-the-end
396 4         11 return BADVERSION($s,$errstr,"Invalid version format (fractional part required)");
397             }
398              
399 349         742 while (isDIGIT($d)) {
400 707         1381 $d++; $j++;
  707         924  
401 707 100 66     1213 if ($d eq '.' && isDIGIT($d-1)) {
402 196 100       464 if ($alpha) {
403 4         12 return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
404             }
405 192 50       376 if ($strict) {
406 0         0 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions must begin with 'v')");
407             }
408 192         282 $d = $s; # start all over again
409 192         650 $qv = TRUE;
410 192         2235 goto dotted_decimal_version;
411             }
412 511 100       894 if ($d eq '_') {
413 32 50       79 if ($strict) {
414 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
415             }
416 32 100       66 if ( $alpha ) {
417 4         11 return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
418             }
419 28 50       76 if ( ! isDIGIT($d+1) ) {
420 0         0 return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)");
421             }
422 28         94 $width = $j;
423 28         104 $d++;
424 28         59 $alpha = TRUE;
425             }
426             }
427             }
428              
429             version_prescan_finish:
430 565         1131 while (isSPACE($d)) {
431 4         7 $d++;
432             }
433              
434 565 50 66     1083 if ($d && !isDIGIT($d) && (! ($d eq ';' || $d eq '}') )) {
      33        
      66        
435             # trailing non-numeric data
436 8         20 return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
437             }
438 557 100 100     1709 if ($saw_decimal > 1 && ($d-1) eq '.') {
439             # no trailing period allowed
440 8         23 return BADVERSION($s,$errstr,"Invalid version format (trailing decimal)");
441             }
442              
443 549 50       1488 if (defined $sqv) {
444 549         930 $$sqv = $qv;
445             }
446 549 50       1594 if (defined $swidth) {
447 549         677 $$swidth = $width;
448             }
449 549 50       871 if (defined $ssaw_decimal) {
450 549         687 $$ssaw_decimal = $saw_decimal;
451             }
452 549 50       823 if (defined $salpha) {
453 549         712 $$salpha = $alpha;
454             }
455 549         1206 return $d;
456             }
457              
458             sub scan_version {
459 617     617 0 1415 my ($s, $rv, $qv) = @_;
460 617         1738 my $start;
461             my $pos;
462 617         0 my $last;
463 617         0 my $errstr;
464 617         797 my $saw_decimal = 0;
465 617         740 my $width = 3;
466 617         842 my $alpha = FALSE;
467 617         802 my $vinf = FALSE;
468 617         747 my @av;
469              
470 617         1859 $s = new charstar $s;
471              
472 617         1504 while (isSPACE($s)) { # leading whitespace is OK
473 4         18 $s++;
474             }
475              
476 617         1675 $last = prescan_version($s, FALSE, \$errstr, \$qv, \$saw_decimal,
477             \$width, \$alpha);
478              
479 617 100       1383 if ($errstr) {
480             # 'undef' is a special case and not an error
481 68 50       125 if ( $s ne 'undef') {
482 68         519 require Carp;
483 68         9662 Carp::croak($errstr);
484             }
485             }
486              
487 549         865 $start = $s;
488 549 100       1055 if ($s eq 'v') {
489 144         260 $s++;
490             }
491 549         853 $pos = $s;
492              
493 549 100       1022 if ( $qv ) {
494 336         826 $$rv->{qv} = $qv;
495             }
496 549 100       1127 if ( $alpha ) {
497 72         199 $$rv->{alpha} = $alpha;
498             }
499 549 100 100     1418 if ( !$qv && $width < 3 ) {
500 16         46 $$rv->{width} = $width;
501             }
502              
503 549   66     1019 while (isDIGIT($pos) || $pos eq '_') {
504 1917         3094 $pos++;
505             }
506 549 50       1100 if (!isALPHA($pos)) {
507 549         709 my $rev;
508              
509 549         657 for (;;) {
510 1400         1890 $rev = 0;
511             {
512             # this is atoi() that delimits on underscores
513 1400         1634 my $end = $pos;
  1400         1874  
514 1400         1824 my $mult = 1;
515 1400         1671 my $orev;
516              
517             # the following if() will only be true after the decimal
518             # point of a version originally created with a bare
519             # floating point number, i.e. not quoted in any way
520             #
521 1400 100 100     3379 if ( !$qv && $s > $start && $saw_decimal == 1 ) {
      66        
522 183         272 $mult *= 100;
523 183         325 while ( $s < $end ) {
524 439 50       668 next if $s eq '_';
525 439         639 $orev = $rev;
526 439         760 $rev += $s * $mult;
527 439         662 $mult /= 10;
528 439 50 33     1291 if ( (abs($orev) > abs($rev))
529             || (abs($rev) > $VERSION_MAX )) {
530 0         0 warn("Integer overflow in version %d",
531             $VERSION_MAX);
532 0         0 $s = $end - 1;
533 0         0 $rev = $VERSION_MAX;
534 0         0 $vinf = 1;
535             }
536 439         683 $s++;
537 439 100       684 if ( $s eq '_' ) {
538 20         33 $s++;
539             }
540             }
541             }
542             else {
543 1217         2470 while (--$end >= $s) {
544 1745 100       3307 next if $end eq '_';
545 1693         2317 $orev = $rev;
546 1693         3186 $rev += $end * $mult;
547 1693         2257 $mult *= 10;
548 1693 100 66     5977 if ( (abs($orev) > abs($rev))
549             || (abs($rev) > $VERSION_MAX )) {
550 28         443 warn("Integer overflow in version");
551 28         201 $end = $s - 1;
552 28         167 $rev = $VERSION_MAX;
553 28         68 $vinf = 1;
554             }
555             }
556             }
557             }
558              
559             # Append revision
560 1400         2697 push @av, $rev;
561 1400 100 66     2940 if ( $vinf ) {
    100 33        
    100          
    50          
    100          
562 28         40 $s = $last;
563 28         60 last;
564             }
565             elsif ( $pos eq '.' ) {
566 801         1466 $s = ++$pos;
567             }
568             elsif ( $pos eq '_' && isDIGIT($pos+1) ) {
569 4         8 $s = ++$pos;
570             }
571             elsif ( $pos eq ',' && isDIGIT($pos+1) ) {
572 0         0 $s = ++$pos;
573             }
574             elsif ( isDIGIT($pos) ) {
575 46         85 $s = $pos;
576             }
577             else {
578 521         717 $s = $pos;
579 521         1672 last;
580             }
581 851 100       2299 if ( $qv ) {
582 668   100     1374 while ( isDIGIT($pos) || $pos eq '_') {
583 948         1748 $pos++;
584             }
585             }
586             else {
587 183         236 my $digits = 0;
588 183   100     357 while ( ( isDIGIT($pos) || $pos eq '_' ) && $digits < 3 ) {
      100        
589 455 100       897 if ( $pos ne '_' ) {
590 439         574 $digits++;
591             }
592 455         762 $pos++;
593             }
594             }
595             }
596             }
597 549 100       1220 if ( $qv ) { # quoted versions always get at least three terms
598 336         502 my $len = $#av;
599             # This for loop appears to trigger a compiler bug on OS X, as it
600             # loops infinitely. Yes, len is negative. No, it makes no sense.
601             # Compiler in question is:
602             # gcc version 3.3 20030304 (Apple Computer, Inc. build 1640)
603             # for ( len = 2 - len; len > 0; len-- )
604             # av_push(MUTABLE_AV(sv), newSViv(0));
605             #
606 336         487 $len = 2 - $len;
607 336         752 while ($len-- > 0) {
608 52         125 push @av, 0;
609             }
610             }
611              
612             # need to save off the current version string for later
613 549 100       1336 if ( $vinf ) {
    50          
614 28         82 $$rv->{original} = "v.Inf";
615 28         71 $$rv->{vinf} = 1;
616             }
617             elsif ( $s > $start ) {
618 521         1264 $$rv->{original} = $start->currstr($s);
619 521 100 100     1793 if ( $qv && $saw_decimal == 1 && $start ne 'v' ) {
      100        
620             # need to insert a v to be consistent
621 4         14 $$rv->{original} = 'v' . $$rv->{original};
622             }
623             }
624             else {
625 0         0 $$rv->{original} = '0';
626 0         0 push(@av, 0);
627             }
628              
629             # And finally, store the AV in the hash
630 549         1454 $$rv->{version} = \@av;
631              
632             # fix RT#19517 - special case 'undef' as string
633 549 50       1160 if ($s eq 'undef') {
634 0         0 $s += 5;
635             }
636              
637 549         2387 return $s;
638             }
639              
640             sub new {
641 657     657 0 125724 my $class = shift;
642 657 50 33     1919 unless (defined $class or $#_ > 1) {
643 0         0 require Carp;
644 0         0 Carp::croak('Usage: version::new(class, version)');
645             }
646              
647 657   66     2806 my $self = bless ({}, ref ($class) || $class);
648 657         1169 my $qv = FALSE;
649              
650 657 100       1563 if ( $#_ == 1 ) { # must be CVS-style
651 8         16 $qv = TRUE;
652             }
653 657         1383 my $value = pop; # always going to be the last element
654              
655 657 50 66     4245 if ( ref($value) && eval('$value->isa("version")') ) {
656             # Can copy the elements directly
657 0         0 $self->{version} = [ @{$value->{version} } ];
  0         0  
658 0 0       0 $self->{qv} = 1 if $value->{qv};
659 0 0       0 $self->{alpha} = 1 if $value->{alpha};
660 0         0 $self->{original} = ''.$value->{original};
661 0         0 return $self;
662             }
663              
664 657 100 100     3544 if ( not defined $value or $value =~ /^undef$/ ) {
665             # RT #19517 - special case for undef comparison
666             # or someone forgot to pass a value
667 32         80 push @{$self->{version}}, 0;
  32         309  
668 32         87 $self->{original} = "0";
669 32         132 return ($self);
670             }
671              
672              
673 625 100       1453 if (ref($value) =~ m/ARRAY|HASH/) {
674 8         41 require Carp;
675 8         798 Carp::croak("Invalid version format (non-numeric data)");
676             }
677              
678 617         1488 $value = _un_vstring($value);
679              
680 617 50       6285 if ($Config{d_setlocale}) {
681 1     1   10 use POSIX qw/locale_h/;
  1         1  
  1         12  
682 1     1   2571 use if $Config{d_setlocale}, 'locale';
  1         2  
  1         1225  
683 617         4373 my $currlocale = setlocale(LC_ALL);
684              
685             # if the current locale uses commas for decimal points, we
686             # just replace commas with decimal places, rather than changing
687             # locales
688 617 50       11564 if ( localeconv()->{decimal_point} eq ',' ) {
689 0         0 $value =~ tr/,/./;
690             }
691             }
692              
693             # exponential notation
694 617 100       5056 if ( $value =~ /\d+.?\d*e[-+]?\d+/ ) {
695 20         268 $value = sprintf("%.9f",$value);
696 20         162 $value =~ s/(0+)$//; # trim trailing zeros
697             }
698              
699 617         1751 my $s = scan_version($value, \$self, $qv);
700              
701 549 50       1208 if ($s) { # must be something left over
702 0         0 warn(sprintf "Version string '%s' contains invalid data; "
703             ."ignoring: '%s'", $value, $s);
704             }
705              
706 549         2744 return ($self);
707             }
708              
709             *parse = \&new;
710              
711             sub numify {
712 56     56 0 241 my ($self) = @_;
713 56 50       127 unless (_verify($self)) {
714 0         0 require Carp;
715 0         0 Carp::croak("Invalid version object");
716             }
717 56   100     211 my $alpha = $self->{alpha} || "";
718 56         75 my $len = $#{$self->{version}};
  56         106  
719 56         97 my $digit = $self->{version}[0];
720 56         243 my $string = sprintf("%d.", $digit );
721              
722 56 100 66     901 if ($alpha and warnings::enabled()) {
723 8         1586 warnings::warn($WARN_CATEGORY, 'alpha->numify() is lossy');
724             }
725              
726 56         220 for ( my $i = 1 ; $i <= $len ; $i++ ) {
727 96         162 $digit = $self->{version}[$i];
728 96         282 $string .= sprintf("%03d", $digit);
729             }
730              
731 56 100       135 if ( $len == 0 ) {
732 4         10 $string .= sprintf("000");
733             }
734              
735 56         303 return $string;
736             }
737              
738             sub normal {
739 32     32 0 102 my ($self) = @_;
740 32 50       77 unless (_verify($self)) {
741 0         0 require Carp;
742 0         0 Carp::croak("Invalid version object");
743             }
744              
745 32         38 my $len = $#{$self->{version}};
  32         67  
746 32         90 my $digit = $self->{version}[0];
747 32         105 my $string = sprintf("v%d", $digit );
748              
749 32         86 for ( my $i = 1 ; $i <= $len ; $i++ ) {
750 56         86 $digit = $self->{version}[$i];
751 56         158 $string .= sprintf(".%d", $digit);
752             }
753              
754 32 100       90 if ( $len <= 2 ) {
755 28         94 for ( $len = 2 - $len; $len != 0; $len-- ) {
756 12         25 $string .= sprintf(".%0d", 0);
757             }
758             }
759              
760 32         1402 return $string;
761             }
762              
763             sub stringify {
764 347     347 0 3956 my ($self) = @_;
765 347 50       634 unless (_verify($self)) {
766 0         0 require Carp;
767 0         0 Carp::croak("Invalid version object");
768             }
769             return exists $self->{original}
770             ? $self->{original}
771             : exists $self->{qv}
772 347 50       7126 ? $self->normal
    100          
773             : $self->numify;
774             }
775              
776             sub to_decimal {
777 4     4 0 34 my ($self) = @_;
778 4         21 return ref($self)->new($self->numify);
779             }
780              
781             sub to_dotted_decimal {
782 4     4 0 25 my ($self) = @_;
783 4         45 return ref($self)->new($self->normal);
784             }
785              
786             sub vcmp {
787 270     270 0 31651 my ($left,$right,$swap) = @_;
788 270 50       718 die "Usage: version::vcmp(lobj, robj, ...)" if @_ < 2;
789 270         497 my $class = ref($left);
790 270 100       1338 unless ( UNIVERSAL::isa($right, $class) ) {
791 108         354 $right = $class->new($right);
792             }
793              
794 266 100       708 if ( $swap ) {
795 20         59 ($left, $right) = ($right, $left);
796             }
797 266 50       710 unless (_verify($left)) {
798 0         0 require Carp;
799 0         0 Carp::croak("Invalid version object");
800             }
801 266 50       468 unless (_verify($right)) {
802 0         0 require Carp;
803 0         0 Carp::croak("Invalid version format");
804             }
805 266         407 my $l = $#{$left->{version}};
  266         626  
806 266         398 my $r = $#{$right->{version}};
  266         436  
807 266 100       640 my $m = $l < $r ? $l : $r;
808 266         735 my $lalpha = $left->is_alpha;
809 266         542 my $ralpha = $right->is_alpha;
810 266         385 my $retval = 0;
811 266         399 my $i = 0;
812 266   100     969 while ( $i <= $m && $retval == 0 ) {
813 634         1172 $retval = $left->{version}[$i] <=> $right->{version}[$i];
814 634         1600 $i++;
815             }
816              
817             # possible match except for trailing 0's
818 266 100 100     818 if ( $retval == 0 && $l != $r ) {
819 40 100       116 if ( $l < $r ) {
820 24   66     101 while ( $i <= $r && $retval == 0 ) {
821 24 100       64 if ( $right->{version}[$i] != 0 ) {
822 20         35 $retval = -1; # not a match after all
823             }
824 24         85 $i++;
825             }
826             }
827             else {
828 16   100     84 while ( $i <= $l && $retval == 0 ) {
829 20 100       70 if ( $left->{version}[$i] != 0 ) {
830 12         16 $retval = +1; # not a match after all
831             }
832 20         49 $i++;
833             }
834             }
835             }
836              
837 266         1483 return $retval;
838             }
839              
840             sub vbool {
841 8     8 0 1026 my ($self) = @_;
842 8         26 return vcmp($self,$self->new("0"),1);
843             }
844              
845             sub vnoop {
846 28     28 0 422 require Carp;
847 28         4204 Carp::croak("operation not supported with version object");
848             }
849              
850             sub is_alpha {
851 544     544 0 799 my ($self) = @_;
852 544         1036 return (exists $self->{alpha});
853             }
854              
855             sub qv {
856 24     24 0 63 my $value = shift;
857 24         49 my $class = $CLASS;
858 24 50       90 if (@_) {
859 24   33     115 $class = ref($value) || $value;
860 24         64 $value = shift;
861             }
862              
863 24         75 $value = _un_vstring($value);
864 24 100       191 $value = 'v'.$value unless $value =~ /(^v|\d+\.\d+\.\d)/;
865 24         102 my $obj = $CLASS->new($value);
866 24         160 return bless $obj, $class;
867             }
868              
869             *declare = \&qv;
870              
871             sub is_qv {
872 36     36 0 61 my ($self) = @_;
873 36         123 return (exists $self->{qv});
874             }
875              
876             sub tuple {
877 4     4 0 32 my ($self) = @_;
878 4         8 return @{ $self->{version} };
  4         36  
879             }
880              
881             sub from_tuple {
882 4     4 0 16 my ($proto, @args) = @_;
883 4   33     25 my $class = ref($proto) || $proto;
884              
885 4         22 my @version = map 0+$_, @args;
886 4 50       18 die if @args < 1;
887 4         131 return bless {
888             version => \@version,
889             qv => !!1,
890             'v' . join('.', @version),
891             }, $class;
892             }
893              
894             sub _verify {
895 967     967   1559 my ($self) = @_;
896 967 50 33     2097 if ( ref($self)
      33        
897 967         4311 && eval { exists $self->{version} }
898             && ref($self->{version}) eq 'ARRAY'
899             ) {
900 967         2457 return 1;
901             }
902             else {
903 0         0 return 0;
904             }
905             }
906              
907             sub _is_non_alphanumeric {
908 184     184   323 my $s = shift;
909 184         659 $s = new charstar $s;
910 184         579 while ($s) {
911 508 100       1022 return 0 if isSPACE($s); # early out
912 504 100 100     860 return 1 unless (isALPHA($s) || isDIGIT($s) || $s =~ /[.-]/);
      100        
913 432         850 $s++;
914             }
915 108         583 return 0;
916             }
917              
918             sub _un_vstring {
919 641     641   1146 my $value = shift;
920             # may be a v-string
921 641 100 66     4325 if ( length($value) >= 1 && $value !~ /[,._]/
      100        
922             && _is_non_alphanumeric($value)) {
923 72         148 my $tvalue;
924 72 50       186 if ( $] >= 5.008_001 ) {
    0          
925 72         176 $tvalue = _find_magic_vstring($value);
926 72 100       244 $value = $tvalue if length $tvalue;
927             }
928             elsif ( $] >= 5.006_000 ) {
929 0         0 $tvalue = sprintf("v%vd",$value);
930 0 0       0 if ( $tvalue =~ /^v\d+(\.\d+)*$/ ) {
931             # must be a v-string
932 0         0 $value = $tvalue;
933             }
934             }
935             }
936 641         1485 return $value;
937             }
938              
939             sub _find_magic_vstring {
940 72     72   165 my $value = shift;
941 72         142 my $tvalue = '';
942 72         469 require B;
943 72         408 my $sv = B::svref_2object(\$value);
944 72 50       446 my $magic = ref($sv) eq 'B::PVMG' ? $sv->MAGIC : undef;
945 72         183 while ( $magic ) {
946 60 50       209 if ( $magic->TYPE eq 'V' ) {
947 60         145 $tvalue = $magic->PTR;
948 60         633 $tvalue =~ s/^v?(.+)$/v$1/;
949 60         124 last;
950             }
951             else {
952 0         0 $magic = $magic->MOREMAGIC;
953             }
954             }
955 72         172 $tvalue =~ tr/_//d;
956 72         307 return $tvalue;
957             }
958              
959             sub _VERSION {
960 103     103   336393 my ($obj, $req) = @_;
961 103   33     664 my $class = ref($obj) || $obj;
962              
963 1     1   4188 no strict 'refs';
  1         2  
  1         542  
964 103 100 100     559 if ( exists $INC{"$class.pm"} and not %{"$class\::"} and $] >= 5.008) {
  85   66     477  
965             # file but no package
966 4         31 require Carp;
967 4         577 Carp::croak( "$class defines neither package nor VERSION"
968             ."--version check failed");
969             }
970              
971 99         7206 my $version = eval "\$$class\::VERSION";
972 99 100       581 if ( defined $version ) {
973 75 50       217 local $^W if $] <= 5.008;
974 75         381 $version = version::vpp->new($version);
975             }
976              
977 91 100       224 if ( defined $req ) {
978 66 100       135 unless ( defined $version ) {
979 8         68 require Carp;
980 8 50       41 my $msg = $] < 5.006
981             ? "$class version $req required--this is only version "
982             : "$class does not define \$$class\::VERSION"
983             ."--version check failed";
984              
985 8 50       41 if ( $ENV{VERSION_DEBUG} ) {
986 0         0 Carp::confess($msg);
987             }
988             else {
989 8         1202 Carp::croak($msg);
990             }
991             }
992              
993 58         138 $req = version::vpp->new($req);
994              
995 58 100       238 if ( $req > $version ) {
996 36         278 require Carp;
997 36 100       105 if ( $req->is_qv ) {
998 8         48 Carp::croak(
999             sprintf ("%s version %s required--".
1000             "this is only version %s", $class,
1001             $req->normal, $version->normal)
1002             );
1003             }
1004             else {
1005 28         106 Carp::croak(
1006             sprintf ("%s version %s required--".
1007             "this is only version %s", $class,
1008             $req->stringify, $version->stringify)
1009             );
1010             }
1011             }
1012             }
1013              
1014 47 100       362 return defined $version ? $version->stringify : undef;
1015             }
1016              
1017             1; #this line is important and will help the module return a true value