File Coverage

blib/lib/version/vpp.pm
Criterion Covered Total %
statement 477 544 87.6
branch 199 278 71.5
condition 110 161 68.3
subroutine 52 53 98.1
pod 0 20 0.0
total 838 1056 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         23 '""' => \&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   265680 );
  1         3  
18              
19             sub new {
20 1418     1418   3114 my ($self, $string) = @_;
21 1418   66     5835 my $class = ref($self) || $self;
22              
23 1418         9499 my $obj = {
24             string => [split(//,$string)],
25             current => 0,
26             };
27 1418         5399 return bless $obj, $class;
28             }
29              
30             sub thischar {
31 34491     34491   58157 my ($self) = @_;
32 34491         44495 my $last = $#{$self->{string}};
  34491         63730  
33 34491         59318 my $curr = $self->{current};
34 34491 100 66     98307 if ($curr >= 0 && $curr <= $last) {
35 26240         80137 return $self->{string}->[$curr];
36             }
37             else {
38 8251         28051 return '';
39             }
40             }
41              
42             sub increment {
43 10273     10273   16397 my ($self) = @_;
44 10273         19169 $self->{current}++;
45             }
46              
47             sub decrement {
48 2962     2962   5072 my ($self) = @_;
49 2962         11386 $self->{current}--;
50             }
51              
52             sub plus {
53 36     36   99 my ($self, $offset) = @_;
54 36         88 my $rself = $self->clone;
55 36         79 $rself->{current} += $offset;
56 36         97 return $rself;
57             }
58              
59             sub minus {
60 524     524   1201 my ($self, $offset) = @_;
61 524         1352 my $rself = $self->clone;
62 524         928 $rself->{current} -= $offset;
63 524         1330 return $rself;
64             }
65              
66             sub multiply {
67 2132     2132   4106 my ($left, $right, $swapped) = @_;
68 2132         3948 my $char = $left->thischar();
69 2132         5376 return $char * $right;
70             }
71              
72             sub spaceship {
73 4589     4589   8567 my ($left, $right, $swapped) = @_;
74 4589 50       9331 unless (ref($right)) { # not an object already
75 0         0 $right = $left->new($right);
76             }
77 4589         16924 return $left->{current} <=> $right->{current};
78             }
79              
80             sub cmp {
81 13065     13065   26649 my ($left, $right, $swapped) = @_;
82 13065 50       26077 unless (ref($right)) { # not an object already
83 13065 100       24546 if (length($right) == 1) { # comparing single character only
84 12448         24809 return $left->thischar cmp $right;
85             }
86 617         1626 $right = $left->new($right);
87             }
88 617         2503 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   8595 my ($left, $right, $swapped) = @_;
99             $right = {
100 4122         24733 string => [@{$left->{string}}],
101             current => $left->{current},
102 4122         5783 };
103 4122         13243 return bless $right, ref($left);
104             }
105              
106             sub currstr {
107 1755     1755   3413 my ($self, $s) = @_;
108 1755         2768 my $curr = $self->{current};
109 1755         2280 my $last = $#{$self->{string}};
  1755         3115  
110 1755 50 66     5286 if (defined($s) && $s->{current} < $last) {
111 0         0 $last = $s->{current};
112             }
113              
114 1755         3455 my $string = join('', @{$self->{string}}[$curr..$last]);
  1755         5081  
115 1755         7013 return $string;
116             }
117              
118             package version::vpp;
119              
120 1     1   983 use 5.006002;
  1         4  
121 1     1   7 use strict;
  1         2  
  1         29  
122 1     1   5 use warnings::register;
  1         1  
  1         126  
123              
124 1     1   8 use Config;
  1         2  
  1         312  
125              
126             our $VERSION = '0.9933';
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         10 '""' => \&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   7 );
  1         2  
159              
160             sub import {
161 1     1   224 no strict 'refs';
  1         3  
  1         361  
162 9     9   129 my ($class) = shift;
163              
164             # Set up any derived class
165 9 100       43 unless ($class eq $CLASS) {
166 4         18 local $^W;
167 4         7 *{$class.'::declare'} = \&{$CLASS.'::declare'};
  4         21  
  4         23  
168 4         9 *{$class.'::qv'} = \&{$CLASS.'::qv'};
  4         22  
  4         14  
169             }
170              
171 9         21 my %args;
172 9 100       32 if (@_) { # any remaining terms are arguments
173 4         11 map { $args{$_} = 1 } @_
  8         21  
174             }
175             else { # no parameters at all on use line
176 5         31 %args =
177             (
178             qv => 1,
179             'UNIVERSAL::VERSION' => 1,
180             );
181             }
182              
183 9         33 my $callpkg = caller();
184              
185 9 100       34 if (exists($args{declare})) {
186 3         10 *{$callpkg.'::declare'} =
187 4     4   1363 sub {return $class->declare(shift) }
188 4 100       6 unless defined(&{$callpkg.'::declare'});
  4         30  
189             }
190              
191 9 50       34 if (exists($args{qv})) {
192 7         61 *{$callpkg.'::qv'} =
193 4     4   940 sub {return $class->qv(shift) }
194 9 100       18 unless defined(&{$callpkg.'::qv'});
  9         128  
195             }
196              
197 9 100       38 if (exists($args{'UNIVERSAL::VERSION'})) {
198 1     1   8 no warnings qw/redefine/;
  1         2  
  1         286  
199             *UNIVERSAL::VERSION
200 5         7 = \&{$CLASS.'::_VERSION'};
  5         32  
201             }
202              
203 9 50       29 if (exists($args{'VERSION'})) {
204 0         0 *{$callpkg.'::VERSION'} = \&{$CLASS.'::_VERSION'};
  0         0  
  0         0  
205             }
206              
207 9 50       32 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       403 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   10 use constant TRUE => 1;
  1         3  
  1         89  
222 1     1   5 use constant FALSE => 0;
  1         2  
  1         2549  
223              
224             sub isDIGIT {
225 12786     12786 0 25012 my ($char) = shift->thischar();
226 12786         46271 return ($char =~ /\d/);
227             }
228              
229             sub isALPHA {
230 1053     1053 0 2344 my ($char) = shift->thischar();
231 1053         3426 return ($char =~ /[a-zA-Z]/);
232             }
233              
234             sub isSPACE {
235 1722     1722 0 4510 my ($char) = shift->thischar();
236 1722         6548 return ($char =~ /\s/);
237             }
238              
239             sub BADVERSION {
240 68     68 0 180 my ($s, $errstr, $error) = @_;
241 68 50       182 if ($errstr) {
242 68         132 $$errstr = $error;
243             }
244 68         341 return $s;
245             }
246              
247             sub prescan_version {
248 617     617 0 1678 my ($s, $strict, $errstr, $sqv, $ssaw_decimal, $swidth, $salpha) = @_;
249 617 50       1503 my $qv = defined $sqv ? $$sqv : FALSE;
250 617 50       1469 my $saw_decimal = defined $ssaw_decimal ? $$ssaw_decimal : 0;
251 617 50       1184 my $width = defined $swidth ? $$swidth : 3;
252 617 50       1274 my $alpha = defined $salpha ? $$salpha : FALSE;
253              
254 617         1052 my $d = $s;
255              
256 617 100 66     1578 if ($qv && isDIGIT($d)) {
257 8         233 goto dotted_decimal_version;
258             }
259              
260 617 100       2103 if ($d eq 'v') { # explicit v-string
261 164         392 $d++;
262 164 100       506 if (isDIGIT($d)) {
263 144         268 $qv = TRUE;
264             }
265             else { # degenerate v-string
266             # requires v1.2.3
267 20         84 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions require at least three parts)");
268             }
269              
270             dotted_decimal_version:
271 344 0 33     1302 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 344         664 while (isDIGIT($d)) { # integer part
277 360         854 $d++;
278             }
279              
280 344 100       831 if ($d eq '.')
281             {
282 340         593 $saw_decimal++;
283 340         606 $d++; # decimal point
284             }
285             else
286             {
287 4 50       13 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         255 goto version_prescan_finish;
293             }
294             }
295              
296             {
297 340         747 my $i = 0;
  340         609  
298 340         543 my $j = 0;
299 340         2177 while (isDIGIT($d)) { # just keep reading
300 736         1176 $i++;
301 736         1202 while (isDIGIT($d)) {
302 948         1751 $d++; $j++;
  948         1373  
303             # maximum 3 digits between decimal
304 948 50 33     2613 if ($strict && $j > 3) {
305 0         0 return BADVERSION($s,$errstr,"Invalid version format (maximum 3 digits between decimals)");
306             }
307             }
308 736 100       1551 if ($d eq '_') {
    100          
    50          
309 52 50       156 if ($strict) {
310 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
311             }
312 52 50       139 if ( $alpha ) {
313 0         0 return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
314             }
315 52         102 $d++;
316 52         93 $alpha = TRUE;
317             }
318             elsif ($d eq '.') {
319 352 50       798 if ($alpha) {
320 0         0 return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
321             }
322 352         525 $saw_decimal++;
323 352         675 $d++;
324             }
325             elsif (!isDIGIT($d)) {
326 332         695 last;
327             }
328 404         895 $j = 0;
329             }
330              
331 340 50 33     1077 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 445         867 my $j = 0;
340             # special $strict case for leading '.' or '0'
341 445 50       1012 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 445 100       1043 if ($d eq '-') {
352 4         14 return BADVERSION($s,$errstr,"Invalid version format (negative version number)");
353             }
354              
355             # consume all of the integer part
356 441         1163 while (isDIGIT($d)) {
357 1801         3047 $d++;
358             }
359              
360             # look for a fractional part
361 441 100 66     957 if ($d eq '.') {
    100 100        
    100 66        
    100          
    50          
362             # we found it, so consume it
363 353         543 $saw_decimal++;
364 353         2133 $d++;
365             }
366             elsif (!$d || $d eq ';' || isSPACE($d) || $d eq '}') {
367 72 50       216 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         3211 goto version_prescan_finish;
373             }
374             elsif ( $d == $s ) {
375             # didn't find either integer or period
376 4         16 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       18 if ($strict) {
    50          
381 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
382             }
383             elsif (isDIGIT($d+1)) {
384 4         17 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         29 return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
393             }
394              
395             # scan the fractional part after the decimal point
396 353 50 100     735 if ($d && !isDIGIT($d) && ($strict || ! ($d eq ';' || isSPACE($d) || $d eq '}') )) {
      33        
      66        
397             # $strict or lax-but-not-the-end
398 4         12 return BADVERSION($s,$errstr,"Invalid version format (fractional part required)");
399             }
400              
401 349         1130 while (isDIGIT($d)) {
402 707         1379 $d++; $j++;
  707         1185  
403 707 100 66     1412 if ($d eq '.' && isDIGIT($d-1)) {
404 196 100       468 if ($alpha) {
405 4         21 return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
406             }
407 192 50       490 if ($strict) {
408 0         0 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions must begin with 'v')");
409             }
410 192         356 $d = $s; # start all over again
411 192         786 $qv = TRUE;
412 192         2522 goto dotted_decimal_version;
413             }
414 511 100       971 if ($d eq '_') {
415 32 50       133 if ($strict) {
416 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
417             }
418 32 100       113 if ( $alpha ) {
419 4         170 return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
420             }
421 28 50       101 if ( ! isDIGIT($d+1) ) {
422 0         0 return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)");
423             }
424 28         165 $width = $j;
425 28         68 $d++;
426 28         71 $alpha = TRUE;
427             }
428             }
429             }
430              
431             version_prescan_finish:
432 565         1317 while (isSPACE($d)) {
433 4         11 $d++;
434             }
435              
436 565 50 66     1423 if ($d && !isDIGIT($d) && (! ($d eq ';' || $d eq '}') )) {
      33        
      66        
437             # trailing non-numeric data
438 8         23 return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
439             }
440 557 100 100     2053 if ($saw_decimal > 1 && ($d-1) eq '.') {
441             # no trailing period allowed
442 8         22 return BADVERSION($s,$errstr,"Invalid version format (trailing decimal)");
443             }
444              
445 549 50       1792 if (defined $sqv) {
446 549         1068 $$sqv = $qv;
447             }
448 549 50       1281 if (defined $swidth) {
449 549         895 $$swidth = $width;
450             }
451 549 50       1032 if (defined $ssaw_decimal) {
452 549         781 $$ssaw_decimal = $saw_decimal;
453             }
454 549 50       1242 if (defined $salpha) {
455 549         812 $$salpha = $alpha;
456             }
457 549         1688 return $d;
458             }
459              
460             sub scan_version {
461 617     617 0 1612 my ($s, $rv, $qv) = @_;
462 617         2610 my $start;
463             my $pos;
464 617         0 my $last;
465 617         0 my $errstr;
466 617         973 my $saw_decimal = 0;
467 617         891 my $width = 3;
468 617         1058 my $alpha = FALSE;
469 617         1132 my $vinf = FALSE;
470 617         998 my @av;
471              
472 617         2391 $s = new charstar $s;
473              
474 617         1618 while (isSPACE($s)) { # leading whitespace is OK
475 4         16 $s++;
476             }
477              
478 617         2039 $last = prescan_version($s, FALSE, \$errstr, \$qv, \$saw_decimal,
479             \$width, \$alpha);
480              
481 617 100       6445 if ($errstr) {
482             # 'undef' is a special case and not an error
483 68 50       173 if ( $s ne 'undef') {
484 68         657 require Carp;
485 68         10747 Carp::croak($errstr);
486             }
487             }
488              
489 549         841 $start = $s;
490 549 100       1171 if ($s eq 'v') {
491 144         284 $s++;
492             }
493 549         1006 $pos = $s;
494              
495 549 100       1184 if ( $qv ) {
496 336         1015 $$rv->{qv} = $qv;
497             }
498 549 100       1125 if ( $alpha ) {
499 72         369 $$rv->{alpha} = $alpha;
500             }
501 549 100 100     1672 if ( !$qv && $width < 3 ) {
502 16         42 $$rv->{width} = $width;
503             }
504              
505 549   66     1302 while (isDIGIT($pos) || $pos eq '_') {
506 1917         3296 $pos++;
507             }
508 549 50       1378 if (!isALPHA($pos)) {
509 549         734 my $rev;
510              
511 549         781 for (;;) {
512 1400         2105 $rev = 0;
513             {
514             # this is atoi() that delimits on underscores
515 1400         1854 my $end = $pos;
  1400         2063  
516 1400         2030 my $mult = 1;
517 1400         1801 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 1400 100 100     4114 if ( !$qv && $s > $start && $saw_decimal == 1 ) {
      66        
524 183         363 $mult *= 100;
525 183         362 while ( $s < $end ) {
526 439 50       801 next if $s eq '_';
527 439         820 $orev = $rev;
528 439         802 $rev += $s * $mult;
529 439         783 $mult /= 10;
530 439 50 33     3944 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 439         792 $s++;
539 439 100       787 if ( $s eq '_' ) {
540 20         38 $s++;
541             }
542             }
543             }
544             else {
545 1217         2909 while (--$end >= $s) {
546 1745 100       3699 next if $end eq '_';
547 1693         2650 $orev = $rev;
548 1693         3758 $rev += $end * $mult;
549 1693         3676 $mult *= 10;
550 1693 100 66     6963 if ( (abs($orev) > abs($rev))
551             || (abs($rev) > $VERSION_MAX )) {
552 28         500 warn("Integer overflow in version");
553 28         218 $end = $s - 1;
554 28         159 $rev = $VERSION_MAX;
555 28         70 $vinf = 1;
556             }
557             }
558             }
559             }
560              
561             # Append revision
562 1400         3303 push @av, $rev;
563 1400 100 66     3201 if ( $vinf ) {
    100 33        
    100          
    50          
    100          
564 28         45 $s = $last;
565 28         91 last;
566             }
567             elsif ( $pos eq '.' ) {
568 801         1629 $s = ++$pos;
569             }
570             elsif ( $pos eq '_' && isDIGIT($pos+1) ) {
571 4         10 $s = ++$pos;
572             }
573             elsif ( $pos eq ',' && isDIGIT($pos+1) ) {
574 0         0 $s = ++$pos;
575             }
576             elsif ( isDIGIT($pos) ) {
577 46         100 $s = $pos;
578             }
579             else {
580 521         829 $s = $pos;
581 521         2101 last;
582             }
583 851 100       2373 if ( $qv ) {
584 668   100     1540 while ( isDIGIT($pos) || $pos eq '_') {
585 948         2049 $pos++;
586             }
587             }
588             else {
589 183         276 my $digits = 0;
590 183   100     1027 while ( ( isDIGIT($pos) || $pos eq '_' ) && $digits < 3 ) {
      100        
591 455 100       962 if ( $pos ne '_' ) {
592 439         700 $digits++;
593             }
594 455         837 $pos++;
595             }
596             }
597             }
598             }
599 549 100       1473 if ( $qv ) { # quoted versions always get at least three terms
600 336         623 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 336         568 $len = 2 - $len;
609 336         1094 while ($len-- > 0) {
610 52         161 push @av, 0;
611             }
612             }
613              
614             # need to save off the current version string for later
615 549 100       1531 if ( $vinf ) {
    50          
616 28         92 $$rv->{original} = "v.Inf";
617 28         76 $$rv->{vinf} = 1;
618             }
619             elsif ( $s > $start ) {
620 521         1604 $$rv->{original} = $start->currstr($s);
621 521 100 100     2037 if ( $qv && $saw_decimal == 1 && $start ne 'v' ) {
      100        
622             # need to insert a v to be consistent
623 4         21 $$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 549         1700 $$rv->{version} = \@av;
633              
634             # fix RT#19517 - special case 'undef' as string
635 549 50       1233 if ($s eq 'undef') {
636 0         0 $s += 5;
637             }
638              
639 549         2845 return $s;
640             }
641              
642             sub new {
643 657     657 0 169461 my $class = shift;
644 657 50 33     2270 unless (defined $class or $#_ > 1) {
645 0         0 require Carp;
646 0         0 Carp::croak('Usage: version::new(class, version)');
647             }
648              
649 657   66     3180 my $self = bless ({}, ref ($class) || $class);
650 657         1332 my $qv = FALSE;
651              
652 657 100       2054 if ( $#_ == 1 ) { # must be CVS-style
653 8         22 $qv = TRUE;
654             }
655 657         1599 my $value = pop; # always going to be the last element
656              
657 657 50 66     5370 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 657 100 100     4191 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         60 push @{$self->{version}}, 0;
  32         314  
670 32         93 $self->{original} = "0";
671 32         146 return ($self);
672             }
673              
674              
675 625 100       1673 if (ref($value) =~ m/ARRAY|HASH/) {
676 8         51 require Carp;
677 8         979 Carp::croak("Invalid version format (non-numeric data)");
678             }
679              
680 617         1795 $value = _un_vstring($value);
681              
682 617 50       8164 if ($Config{d_setlocale}) {
683 1     1   10 use POSIX qw/locale_h/;
  1         2  
  1         32  
684 1     1   2541 use if $Config{d_setlocale}, 'locale';
  1         3  
  1         1304  
685 617         5208 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 617 50       16173 if ( localeconv()->{decimal_point} eq ',' ) {
691 0         0 $value =~ tr/,/./;
692             }
693             }
694              
695             # exponential notation
696 617 100       5354 if ( $value =~ /\d+.?\d*e[-+]?\d+/ ) {
697 20         323 $value = sprintf("%.9f",$value);
698 20         173 $value =~ s/(0+)$//; # trim trailing zeros
699             }
700              
701 617         2124 my $s = scan_version($value, \$self, $qv);
702              
703 549 50       1416 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 549         3275 return ($self);
709             }
710              
711             *parse = \&new;
712              
713             sub numify {
714 56     56 0 298 my ($self) = @_;
715 56 50       181 unless (_verify($self)) {
716 0         0 require Carp;
717 0         0 Carp::croak("Invalid version object");
718             }
719 56   100     243 my $alpha = $self->{alpha} || "";
720 56         137 my $len = $#{$self->{version}};
  56         249  
721 56         130 my $digit = $self->{version}[0];
722 56         238 my $string = sprintf("%d.", $digit );
723              
724 56 100 66     1099 if ($alpha and warnings::enabled()) {
725 8         1507 warnings::warn($WARN_CATEGORY, 'alpha->numify() is lossy');
726             }
727              
728 56         243 for ( my $i = 1 ; $i <= $len ; $i++ ) {
729 96         160 $digit = $self->{version}[$i];
730 96         302 $string .= sprintf("%03d", $digit);
731             }
732              
733 56 100       139 if ( $len == 0 ) {
734 4         8 $string .= sprintf("000");
735             }
736              
737 56         493 return $string;
738             }
739              
740             sub normal {
741 32     32 0 103 my ($self) = @_;
742 32 50       84 unless (_verify($self)) {
743 0         0 require Carp;
744 0         0 Carp::croak("Invalid version object");
745             }
746              
747 32         75 my $len = $#{$self->{version}};
  32         79  
748 32         70 my $digit = $self->{version}[0];
749 32         114 my $string = sprintf("v%d", $digit );
750              
751 32         94 for ( my $i = 1 ; $i <= $len ; $i++ ) {
752 56         97 $digit = $self->{version}[$i];
753 56         146 $string .= sprintf(".%d", $digit);
754             }
755              
756 32 100       99 if ( $len <= 2 ) {
757 28         89 for ( $len = 2 - $len; $len != 0; $len-- ) {
758 12         31 $string .= sprintf(".%0d", 0);
759             }
760             }
761              
762 32         1494 return $string;
763             }
764              
765             sub stringify {
766 347     347 0 5182 my ($self) = @_;
767 347 50       790 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 347 50       20441 ? $self->normal
    100          
775             : $self->numify;
776             }
777              
778             sub to_decimal {
779 4     4 0 30 my ($self) = @_;
780 4         18 return ref($self)->new($self->numify);
781             }
782              
783             sub to_dotted_decimal {
784 4     4 0 27 my ($self) = @_;
785 4         21 return ref($self)->new($self->normal);
786             }
787              
788             sub vcmp {
789 270     270 0 44782 my ($left,$right,$swap) = @_;
790 270 50       1002 die "Usage: version::vcmp(lobj, robj, ...)" if @_ < 2;
791 270         552 my $class = ref($left);
792 270 100       1586 unless ( UNIVERSAL::isa($right, $class) ) {
793 108         438 $right = $class->new($right);
794             }
795              
796 266 100       689 if ( $swap ) {
797 20         89 ($left, $right) = ($right, $left);
798             }
799 266 50       730 unless (_verify($left)) {
800 0         0 require Carp;
801 0         0 Carp::croak("Invalid version object");
802             }
803 266 50       507 unless (_verify($right)) {
804 0         0 require Carp;
805 0         0 Carp::croak("Invalid version format");
806             }
807 266         405 my $l = $#{$left->{version}};
  266         617  
808 266         412 my $r = $#{$right->{version}};
  266         550  
809 266 100       650 my $m = $l < $r ? $l : $r;
810 266         951 my $lalpha = $left->is_alpha;
811 266         553 my $ralpha = $right->is_alpha;
812 266         548 my $retval = 0;
813 266         477 my $i = 0;
814 266   100     1354 while ( $i <= $m && $retval == 0 ) {
815 634         1411 $retval = $left->{version}[$i] <=> $right->{version}[$i];
816 634         2201 $i++;
817             }
818              
819             # possible match except for trailing 0's
820 266 100 100     1079 if ( $retval == 0 && $l != $r ) {
821 40 100       101 if ( $l < $r ) {
822 24   66     108 while ( $i <= $r && $retval == 0 ) {
823 24 100       67 if ( $right->{version}[$i] != 0 ) {
824 20         37 $retval = -1; # not a match after all
825             }
826 24         59 $i++;
827             }
828             }
829             else {
830 16   100     99 while ( $i <= $l && $retval == 0 ) {
831 20 100       59 if ( $left->{version}[$i] != 0 ) {
832 12         21 $retval = +1; # not a match after all
833             }
834 20         55 $i++;
835             }
836             }
837             }
838              
839 266         1896 return $retval;
840             }
841              
842             sub vbool {
843 8     8 0 1345 my ($self) = @_;
844 8         34 return vcmp($self,$self->new("0"),1);
845             }
846              
847             sub vnoop {
848 28     28 0 510 require Carp;
849 28         5163 Carp::croak("operation not supported with version object");
850             }
851              
852             sub is_alpha {
853 544     544 0 1016 my ($self) = @_;
854 544         1300 return (exists $self->{alpha});
855             }
856              
857             sub qv {
858 24     24 0 74 my $value = shift;
859 24         64 my $class = $CLASS;
860 24 50       83 if (@_) {
861 24   33     124 $class = ref($value) || $value;
862 24         58 $value = shift;
863             }
864              
865 24         77 $value = _un_vstring($value);
866 24 100       203 $value = 'v'.$value unless $value =~ /(^v|\d+\.\d+\.\d)/;
867 24         98 my $obj = $CLASS->new($value);
868 24         253 return bless $obj, $class;
869             }
870              
871             *declare = \&qv;
872              
873             sub is_qv {
874 36     36 0 71 my ($self) = @_;
875 36         1605 return (exists $self->{qv});
876             }
877              
878             sub tuple {
879 4     4 0 34 my ($self) = @_;
880 4         7 return @{ $self->{version} };
  4         55  
881             }
882              
883             sub from_tuple {
884 4     4 0 18 my ($proto, @args) = @_;
885 4   33     29 my $class = ref($proto) || $proto;
886              
887 4         25 my @version = map 0+$_, @args;
888 4 50       17 die if @args < 1;
889 4         117 return bless {
890             version => \@version,
891             qv => !!1,
892             'v' . join('.', @version),
893             }, $class;
894             }
895              
896             sub _verify {
897 967     967   1844 my ($self) = @_;
898 967 50 33     2588 if ( ref($self)
      33        
899 967         5415 && eval { exists $self->{version} }
900             && ref($self->{version}) eq 'ARRAY'
901             ) {
902 967         4716 return 1;
903             }
904             else {
905 0         0 return 0;
906             }
907             }
908              
909             sub _is_non_alphanumeric {
910 184     184   388 my $s = shift;
911 184         671 $s = new charstar $s;
912 184         725 while ($s) {
913 508 100       1193 return 0 if isSPACE($s); # early out
914 504 100 100     1009 return 1 unless (isALPHA($s) || isDIGIT($s) || $s =~ /[.-]/);
      100        
915 432         1061 $s++;
916             }
917 108         697 return 0;
918             }
919              
920             sub _un_vstring {
921 641     641   1562 my $value = shift;
922             # may be a v-string
923 641 100 66     4930 if ( length($value) >= 1 && $value !~ /[,._]/
      100        
924             && _is_non_alphanumeric($value)) {
925 72         116 my $tvalue;
926 72 50       171 if ( $] >= 5.008_001 ) {
    0          
927 72         175 $tvalue = _find_magic_vstring($value);
928 72 100       225 $value = $tvalue if length $tvalue;
929             }
930             elsif ( $] >= 5.006_000 ) {
931 0         0 $tvalue = sprintf("v%vd",$value);
932 0 0       0 if ( $tvalue =~ /^v\d+(\.\d+)*$/ ) {
933             # must be a v-string
934 0         0 $value = $tvalue;
935             }
936             }
937             }
938 641         1576 return $value;
939             }
940              
941             sub _find_magic_vstring {
942 72     72   208 my $value = shift;
943 72         142 my $tvalue = '';
944 72         571 require B;
945 72         488 my $sv = B::svref_2object(\$value);
946 72 50       391 my $magic = ref($sv) eq 'B::PVMG' ? $sv->MAGIC : undef;
947 72         193 while ( $magic ) {
948 60 50       331 if ( $magic->TYPE eq 'V' ) {
949 60         162 $tvalue = $magic->PTR;
950 60         713 $tvalue =~ s/^v?(.+)$/v$1/;
951 60         149 last;
952             }
953             else {
954 0         0 $magic = $magic->MOREMAGIC;
955             }
956             }
957 72         177 $tvalue =~ tr/_//d;
958 72         283 return $tvalue;
959             }
960              
961             sub _VERSION {
962 103     103   325536 my ($obj, $req) = @_;
963 103   33     802 my $class = ref($obj) || $obj;
964              
965 1     1   3548 no strict 'refs';
  1         2  
  1         457  
966 103 100 100     610 if ( exists $INC{"$class.pm"} and not %{"$class\::"} and $] >= 5.008) {
  85   66     571  
967             # file but no package
968 4         36 require Carp;
969 4         766 Carp::croak( "$class defines neither package nor VERSION"
970             ."--version check failed");
971             }
972              
973 99         8249 my $version = eval "\$$class\::VERSION";
974 99 100       636 if ( defined $version ) {
975 75 50       243 local $^W if $] <= 5.008;
976 75         438 $version = version::vpp->new($version);
977             }
978              
979 91 100       257 if ( defined $req ) {
980 66 100       216 unless ( defined $version ) {
981 8         66 require Carp;
982 8 50       57 my $msg = $] < 5.006
983             ? "$class version $req required--this is only version "
984             : "$class does not define \$$class\::VERSION"
985             ."--version check failed";
986              
987 8 50       34 if ( $ENV{VERSION_DEBUG} ) {
988 0         0 Carp::confess($msg);
989             }
990             else {
991 8         2243 Carp::croak($msg);
992             }
993             }
994              
995 58         232 $req = version::vpp->new($req);
996              
997 58 100       309 if ( $req > $version ) {
998 36         352 require Carp;
999 36 100       119 if ( $req->is_qv ) {
1000 8         56 Carp::croak(
1001             sprintf ("%s version %s required--".
1002             "this is only version %s", $class,
1003             $req->normal, $version->normal)
1004             );
1005             }
1006             else {
1007 28         116 Carp::croak(
1008             sprintf ("%s version %s required--".
1009             "this is only version %s", $class,
1010             $req->stringify, $version->stringify)
1011             );
1012             }
1013             }
1014             }
1015              
1016 47 100       398 return defined $version ? $version->stringify : undef;
1017             }
1018              
1019             1; #this line is important and will help the module return a true value