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         13 '""' => \&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   156647 );
  1         3  
18              
19             sub new {
20 1344     1344   2426 my ($self, $string) = @_;
21 1344   66     3360 my $class = ref($self) || $self;
22              
23 1344         6749 my $obj = {
24             string => [split(//,$string)],
25             current => 0,
26             };
27 1344         3919 return bless $obj, $class;
28             }
29              
30             sub thischar {
31 32140     32140   43324 my ($self) = @_;
32 32140         37072 my $last = $#{$self->{string}};
  32140         49628  
33 32140         45188 my $curr = $self->{current};
34 32140 100 66     84282 if ($curr >= 0 && $curr <= $last) {
35 24444         65008 return $self->{string}->[$curr];
36             }
37             else {
38 7696         19593 return '';
39             }
40             }
41              
42             sub increment {
43 9656     9656   14110 my ($self) = @_;
44 9656         17168 $self->{current}++;
45             }
46              
47             sub decrement {
48 2832     2832   4432 my ($self) = @_;
49 2832         5874 $self->{current}--;
50             }
51              
52             sub plus {
53 36     36   69 my ($self, $offset) = @_;
54 36         64 my $rself = $self->clone;
55 36         56 $rself->{current} += $offset;
56 36         82 return $rself;
57             }
58              
59             sub minus {
60 512     512   880 my ($self, $offset) = @_;
61 512         911 my $rself = $self->clone;
62 512         794 $rself->{current} -= $offset;
63 512         1116 return $rself;
64             }
65              
66             sub multiply {
67 1948     1948   3177 my ($left, $right, $swapped) = @_;
68 1948         3004 my $char = $left->thischar();
69 1948         4146 return $char * $right;
70             }
71              
72             sub spaceship {
73 4192     4192   6977 my ($left, $right, $swapped) = @_;
74 4192 50       7319 unless (ref($right)) { # not an object already
75 0         0 $right = $left->new($right);
76             }
77 4192         11341 return $left->{current} <=> $right->{current};
78             }
79              
80             sub cmp {
81 11856     11856   20386 my ($left, $right, $swapped) = @_;
82 11856 50       19488 unless (ref($right)) { # not an object already
83 11856 100       20877 if (length($right) == 1) { # comparing single character only
84 11276         17549 return $left->thischar cmp $right;
85             }
86 580         1162 $right = $left->new($right);
87             }
88 580         1066 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   6210 my ($left, $right, $swapped) = @_;
99             $right = {
100 3888         15898 string => [@{$left->{string}}],
101             current => $left->{current},
102 3888         5541 };
103 3888         9392 return bless $right, ref($left);
104             }
105              
106             sub currstr {
107 1644     1644   2557 my ($self, $s) = @_;
108 1644         2204 my $curr = $self->{current};
109 1644         1942 my $last = $#{$self->{string}};
  1644         2698  
110 1644 50 66     4083 if (defined($s) && $s->{current} < $last) {
111 0         0 $last = $s->{current};
112             }
113              
114 1644         2677 my $string = join('', @{$self->{string}}[$curr..$last]);
  1644         3581  
115 1644         4942 return $string;
116             }
117              
118             package version::vpp;
119              
120 1     1   929 use 5.006002;
  1         3  
121 1     1   7 use strict;
  1         2  
  1         23  
122 1     1   5 use warnings::register;
  1         2  
  1         163  
123              
124 1     1   7 use Config;
  1         2  
  1         305  
125              
126             our $VERSION = '0.9930';
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         19 '""' => \&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   8 );
  1         2  
159              
160             sub import {
161 1     1   207 no strict 'refs';
  1         6  
  1         277  
162 9     9   112 my ($class) = shift;
163              
164             # Set up any derived class
165 9 100       42 unless ($class eq $CLASS) {
166 4         24 local $^W;
167 4         8 *{$class.'::declare'} = \&{$CLASS.'::declare'};
  4         26  
  4         22  
168 4         12 *{$class.'::qv'} = \&{$CLASS.'::qv'};
  4         22  
  4         16  
169             }
170              
171 9         16 my %args;
172 9 100       35 if (@_) { # any remaining terms are arguments
173 4         12 map { $args{$_} = 1 } @_
  8         21  
174             }
175             else { # no parameters at all on use line
176 5         29 %args =
177             (
178             qv => 1,
179             'UNIVERSAL::VERSION' => 1,
180             );
181             }
182              
183 9         84 my $callpkg = caller();
184              
185 9 100       31 if (exists($args{declare})) {
186 3         12 *{$callpkg.'::declare'} =
187 4     4   688 sub {return $class->declare(shift) }
188 4 100       14 unless defined(&{$callpkg.'::declare'});
  4         40  
189             }
190              
191 9 50       32 if (exists($args{qv})) {
192 7         31 *{$callpkg.'::qv'} =
193 4     4   684 sub {return $class->qv(shift) }
194 9 100       18 unless defined(&{$callpkg.'::qv'});
  9         77  
195             }
196              
197 9 100       28 if (exists($args{'UNIVERSAL::VERSION'})) {
198 1     1   7 no warnings qw/redefine/;
  1         2  
  1         212  
199             *UNIVERSAL::VERSION
200 5         12 = \&{$CLASS.'::_VERSION'};
  5         22  
201             }
202              
203 9 50       35 if (exists($args{'VERSION'})) {
204 0         0 *{$callpkg.'::VERSION'} = \&{$CLASS.'::_VERSION'};
  0         0  
  0         0  
205             }
206              
207 9 50       25 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       306 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   7 use constant TRUE => 1;
  1         2  
  1         73  
222 1     1   30 use constant FALSE => 0;
  1         2  
  1         2207  
223              
224             sub isDIGIT {
225 12100     12100 0 18654 my ($char) = shift->thischar();
226 12100         38836 return ($char =~ /\d/);
227             }
228              
229             sub isALPHA {
230 1016     1016 0 1718 my ($char) = shift->thischar();
231 1016         3071 return ($char =~ /[a-zA-Z]/);
232             }
233              
234             sub isSPACE {
235 1648     1648 0 2975 my ($char) = shift->thischar();
236 1648         4853 return ($char =~ /\s/);
237             }
238              
239             sub BADVERSION {
240 68     68 0 138 my ($s, $errstr, $error) = @_;
241 68 50       146 if ($errstr) {
242 68         131 $$errstr = $error;
243             }
244 68         240 return $s;
245             }
246              
247             sub prescan_version {
248 580     580 0 1178 my ($s, $strict, $errstr, $sqv, $ssaw_decimal, $swidth, $salpha) = @_;
249 580 50       1058 my $qv = defined $sqv ? $$sqv : FALSE;
250 580 50       999 my $saw_decimal = defined $ssaw_decimal ? $$ssaw_decimal : 0;
251 580 50       973 my $width = defined $swidth ? $$swidth : 3;
252 580 50       1061 my $alpha = defined $salpha ? $$salpha : FALSE;
253              
254 580         762 my $d = $s;
255              
256 580 100 66     1187 if ($qv && isDIGIT($d)) {
257 8         168 goto dotted_decimal_version;
258             }
259              
260 580 100       1360 if ($d eq 'v') { # explicit v-string
261 152         337 $d++;
262 152 100       301 if (isDIGIT($d)) {
263 132         214 $qv = TRUE;
264             }
265             else { # degenerate v-string
266             # requires v1.2.3
267 20         92 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     898 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         579 while (isDIGIT($d)) { # integer part
277 348         768 $d++;
278             }
279              
280 332 100       662 if ($d eq '.')
281             {
282 328         475 $saw_decimal++;
283 328         519 $d++; # decimal point
284             }
285             else
286             {
287 4 50       30 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         110 goto version_prescan_finish;
293             }
294             }
295              
296             {
297 328         478 my $i = 0;
  328         415  
298 328         432 my $j = 0;
299 328         577 while (isDIGIT($d)) { # just keep reading
300 708         1130 $i++;
301 708         1115 while (isDIGIT($d)) {
302 920         1736 $d++; $j++;
  920         1117  
303             # maximum 3 digits between decimal
304 920 50 33     1978 if ($strict && $j > 3) {
305 0         0 return BADVERSION($s,$errstr,"Invalid version format (maximum 3 digits between decimals)");
306             }
307             }
308 708 100       1336 if ($d eq '_') {
    100          
    50          
309 52 50       114 if ($strict) {
310 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
311             }
312 52 50       95 if ( $alpha ) {
313 0         0 return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
314             }
315 52         89 $d++;
316 52         73 $alpha = TRUE;
317             }
318             elsif ($d eq '.') {
319 336 50       642 if ($alpha) {
320 0         0 return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
321             }
322 336         418 $saw_decimal++;
323 336         500 $d++;
324             }
325             elsif (!isDIGIT($d)) {
326 320         498 last;
327             }
328 388         739 $j = 0;
329             }
330              
331 328 50 33     764 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         641 my $j = 0;
340             # special $strict case for leading '.' or '0'
341 420 50       758 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       726 if ($d eq '-') {
352 4         44 return BADVERSION($s,$errstr,"Invalid version format (negative version number)");
353             }
354              
355             # consume all of the integer part
356 416         912 while (isDIGIT($d)) {
357 1776         3467 $d++;
358             }
359              
360             # look for a fractional part
361 416 100 66     828 if ($d eq '.') {
    100 100        
    100 66        
    100          
    50          
362             # we found it, so consume it
363 328         495 $saw_decimal++;
364 328         641 $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         1856 goto version_prescan_finish;
373             }
374             elsif ( $d == $s ) {
375             # didn't find either integer or period
376 4         25 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       17 if ($strict) {
    50          
381 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
382             }
383             elsif (isDIGIT($d+1)) {
384 4         10 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         32 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     604 if ($d && !isDIGIT($d) && ($strict || ! ($d eq ';' || isSPACE($d) || $d eq '}') )) {
      33        
      66        
397             # $strict or lax-but-not-the-end
398 4         16 return BADVERSION($s,$errstr,"Invalid version format (fractional part required)");
399             }
400              
401 324         675 while (isDIGIT($d)) {
402 588         1139 $d++; $j++;
  588         766  
403 588 100 66     901 if ($d eq '.' && isDIGIT($d-1)) {
404 196 100       434 if ($alpha) {
405 4         9 return BADVERSION($s,$errstr,"Invalid version format (underscores before decimal)");
406             }
407 192 50       319 if ($strict) {
408 0         0 return BADVERSION($s,$errstr,"Invalid version format (dotted-decimal versions must begin with 'v')");
409             }
410 192         277 $d = $s; # start all over again
411 192         461 $qv = TRUE;
412 192         1392 goto dotted_decimal_version;
413             }
414 392 100       700 if ($d eq '_') {
415 32 50       83 if ($strict) {
416 0         0 return BADVERSION($s,$errstr,"Invalid version format (no underscores)");
417             }
418 32 100       86 if ( $alpha ) {
419 4         12 return BADVERSION($s,$errstr,"Invalid version format (multiple underscores)");
420             }
421 28 50       58 if ( ! isDIGIT($d+1) ) {
422 0         0 return BADVERSION($s,$errstr,"Invalid version format (misplaced underscore)");
423             }
424 28         75 $width = $j;
425 28         51 $d++;
426 28         59 $alpha = TRUE;
427             }
428             }
429             }
430              
431             version_prescan_finish:
432 528         1001 while (isSPACE($d)) {
433 4         11 $d++;
434             }
435              
436 528 50 66     1030 if ($d && !isDIGIT($d) && (! ($d eq ';' || $d eq '}') )) {
      33        
      66        
437             # trailing non-numeric data
438 8         22 return BADVERSION($s,$errstr,"Invalid version format (non-numeric data)");
439             }
440 520 100 100     1417 if ($saw_decimal > 1 && ($d-1) eq '.') {
441             # no trailing period allowed
442 8         29 return BADVERSION($s,$errstr,"Invalid version format (trailing decimal)");
443             }
444              
445 512 50       1289 if (defined $sqv) {
446 512         826 $$sqv = $qv;
447             }
448 512 50       923 if (defined $swidth) {
449 512         702 $$swidth = $width;
450             }
451 512 50       923 if (defined $ssaw_decimal) {
452 512         687 $$ssaw_decimal = $saw_decimal;
453             }
454 512 50       867 if (defined $salpha) {
455 512         686 $$salpha = $alpha;
456             }
457 512         975 return $d;
458             }
459              
460             sub scan_version {
461 580     580 0 1172 my ($s, $rv, $qv) = @_;
462 580         1471 my $start;
463             my $pos;
464 580         0 my $last;
465 580         0 my $errstr;
466 580         856 my $saw_decimal = 0;
467 580         688 my $width = 3;
468 580         718 my $alpha = FALSE;
469 580         1200 my $vinf = FALSE;
470 580         797 my @av;
471              
472 580         1489 $s = new charstar $s;
473              
474 580         1193 while (isSPACE($s)) { # leading whitespace is OK
475 4         16 $s++;
476             }
477              
478 580         1393 $last = prescan_version($s, FALSE, \$errstr, \$qv, \$saw_decimal,
479             \$width, \$alpha);
480              
481 580 100       1180 if ($errstr) {
482             # 'undef' is a special case and not an error
483 68 50       124 if ( $s ne 'undef') {
484 68         351 require Carp;
485 68         7100 Carp::croak($errstr);
486             }
487             }
488              
489 512         675 $start = $s;
490 512 100       945 if ($s eq 'v') {
491 132         258 $s++;
492             }
493 512         738 $pos = $s;
494              
495 512 100       959 if ( $qv ) {
496 324         679 $$rv->{qv} = $qv;
497             }
498 512 100       826 if ( $alpha ) {
499 72         166 $$rv->{alpha} = $alpha;
500             }
501 512 100 100     1339 if ( !$qv && $width < 3 ) {
502 16         53 $$rv->{width} = $width;
503             }
504              
505 512   66     860 while (isDIGIT($pos) || $pos eq '_') {
506 1880         3486 $pos++;
507             }
508 512 50       987 if (!isALPHA($pos)) {
509 512         700 my $rev;
510              
511 512         669 for (;;) {
512 1292         1833 $rev = 0;
513             {
514             # this is atoi() that delimits on underscores
515 1292         1487 my $end = $pos;
  1292         1746  
516 1292         1717 my $mult = 1;
517 1292         1541 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     3009 if ( !$qv && $s > $start && $saw_decimal == 1 ) {
      66        
524 140         199 $mult *= 100;
525 140         275 while ( $s < $end ) {
526 320 50       548 next if $s eq '_';
527 320         440 $orev = $rev;
528 320         538 $rev += $s * $mult;
529 320         488 $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         539 $s++;
539 320 100       491 if ( $s eq '_' ) {
540 20         37 $s++;
541             }
542             }
543             }
544             else {
545 1152         2084 while (--$end >= $s) {
546 1680 100       2859 next if $end eq '_';
547 1628         2408 $orev = $rev;
548 1628         4333 $rev += $end * $mult;
549 1628         2163 $mult *= 10;
550 1628 100 66     5597 if ( (abs($orev) > abs($rev))
551             || (abs($rev) > $VERSION_MAX )) {
552 28         338 warn("Integer overflow in version");
553 28         186 $end = $s - 1;
554 28         143 $rev = $VERSION_MAX;
555 28         69 $vinf = 1;
556             }
557             }
558             }
559             }
560              
561             # Append revision
562 1292         2496 push @av, $rev;
563 1292 100 66     2669 if ( $vinf ) {
    100 33        
    100          
    50          
    100          
564 28         41 $s = $last;
565 28         62 last;
566             }
567             elsif ( $pos eq '.' ) {
568 748         1441 $s = ++$pos;
569             }
570             elsif ( $pos eq '_' && isDIGIT($pos+1) ) {
571 4         8 $s = ++$pos;
572             }
573             elsif ( $pos eq ',' && isDIGIT($pos+1) ) {
574 0         0 $s = ++$pos;
575             }
576             elsif ( isDIGIT($pos) ) {
577 28         50 $s = $pos;
578             }
579             else {
580 484         751 $s = $pos;
581 484         1184 last;
582             }
583 780 100       1809 if ( $qv ) {
584 640   100     1228 while ( isDIGIT($pos) || $pos eq '_') {
585 920         1862 $pos++;
586             }
587             }
588             else {
589 140         207 my $digits = 0;
590 140   100     267 while ( ( isDIGIT($pos) || $pos eq '_' ) && $digits < 3 ) {
      100        
591 336 100       685 if ( $pos ne '_' ) {
592 320         454 $digits++;
593             }
594 336         554 $pos++;
595             }
596             }
597             }
598             }
599 512 100       1148 if ( $qv ) { # quoted versions always get at least three terms
600 324         486 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         472 $len = 2 - $len;
609 324         696 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       1186 if ( $vinf ) {
    50          
616 28         69 $$rv->{original} = "v.Inf";
617 28         53 $$rv->{vinf} = 1;
618             }
619             elsif ( $s > $start ) {
620 484         961 $$rv->{original} = $start->currstr($s);
621 484 100 100     1565 if ( $qv && $saw_decimal == 1 && $start ne 'v' ) {
      100        
622             # need to insert a v to be consistent
623 4         31 $$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         970 $$rv->{version} = \@av;
633              
634             # fix RT#19517 - special case 'undef' as string
635 512 50       1018 if ($s eq 'undef') {
636 0         0 $s += 5;
637             }
638              
639 512         1826 return $s;
640             }
641              
642             sub new {
643 620     620 0 96624 my $class = shift;
644 620 50 33     1623 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     2382 my $self = bless ({}, ref ($class) || $class);
650 620         995 my $qv = FALSE;
651              
652 620 100       1531 if ( $#_ == 1 ) { # must be CVS-style
653 8         25 $qv = TRUE;
654             }
655 620         1201 my $value = pop; # always going to be the last element
656              
657 620 50 66     3467 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     2850 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         55 push @{$self->{version}}, 0;
  32         288  
670 32         70 $self->{original} = "0";
671 32         141 return ($self);
672             }
673              
674              
675 588 100       1329 if (ref($value) =~ m/ARRAY|HASH/) {
676 8         42 require Carp;
677 8         756 Carp::croak("Invalid version format (non-numeric data)");
678             }
679              
680 580         1163 $value = _un_vstring($value);
681              
682 580 50       4265 if ($Config{d_setlocale}) {
683 1     1   8 use POSIX qw/locale_h/;
  1         2  
  1         5  
684 1     1   2561 use if $Config{d_setlocale}, 'locale';
  1         14  
  1         6  
685 580         2454 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       4050 if ( localeconv()->{decimal_point} eq ',' ) {
691 0         0 $value =~ tr/,/./;
692             }
693             }
694              
695             # exponential notation
696 580 100       2452 if ( $value =~ /\d+.?\d*e[-+]?\d+/ ) {
697 20         279 $value = sprintf("%.9f",$value);
698 20         152 $value =~ s/(0+)$//; # trim trailing zeros
699             }
700              
701 580         1358 my $s = scan_version($value, \$self, $qv);
702              
703 512 50       1035 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         2636 return ($self);
709             }
710              
711             *parse = \&new;
712              
713             sub numify {
714 52     52 0 289 my ($self) = @_;
715 52 50       115 unless (_verify($self)) {
716 0         0 require Carp;
717 0         0 Carp::croak("Invalid version object");
718             }
719 52   100     205 my $alpha = $self->{alpha} || "";
720 52         75 my $len = $#{$self->{version}};
  52         104  
721 52         100 my $digit = $self->{version}[0];
722 52         198 my $string = sprintf("%d.", $digit );
723              
724 52 100 66     807 if ($alpha and warnings::enabled()) {
725 8         898 warnings::warn($WARN_CATEGORY, 'alpha->numify() is lossy');
726             }
727              
728 52         487 for ( my $i = 1 ; $i <= $len ; $i++ ) {
729 88         137 $digit = $self->{version}[$i];
730 88         270 $string .= sprintf("%03d", $digit);
731             }
732              
733 52 100       117 if ( $len == 0 ) {
734 4         11 $string .= sprintf("000");
735             }
736              
737 52         391 return $string;
738             }
739              
740             sub normal {
741 24     24 0 105 my ($self) = @_;
742 24 50       51 unless (_verify($self)) {
743 0         0 require Carp;
744 0         0 Carp::croak("Invalid version object");
745             }
746              
747 24         39 my $len = $#{$self->{version}};
  24         66  
748 24         53 my $digit = $self->{version}[0];
749 24         80 my $string = sprintf("v%d", $digit );
750              
751 24         69 for ( my $i = 1 ; $i <= $len ; $i++ ) {
752 36         58 $digit = $self->{version}[$i];
753 36         91 $string .= sprintf(".%d", $digit);
754             }
755              
756 24 50       52 if ( $len <= 2 ) {
757 24         56 for ( $len = 2 - $len; $len != 0; $len-- ) {
758 12         58 $string .= sprintf(".%0d", 0);
759             }
760             }
761              
762 24         1039 return $string;
763             }
764              
765             sub stringify {
766 328     328 0 1259 my ($self) = @_;
767 328 50       542 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       5618 ? $self->normal
    50          
775             : $self->numify;
776             }
777              
778             sub vcmp {
779 260     260 0 20820 my ($left,$right,$swap) = @_;
780 260 50       748 die "Usage: version::vcmp(lobj, robj, ...)" if @_ < 2;
781 260         464 my $class = ref($left);
782 260 100       1013 unless ( UNIVERSAL::isa($right, $class) ) {
783 104         304 $right = $class->new($right);
784             }
785              
786 256 100       539 if ( $swap ) {
787 20         49 ($left, $right) = ($right, $left);
788             }
789 256 50       484 unless (_verify($left)) {
790 0         0 require Carp;
791 0         0 Carp::croak("Invalid version object");
792             }
793 256 50       424 unless (_verify($right)) {
794 0         0 require Carp;
795 0         0 Carp::croak("Invalid version format");
796             }
797 256         345 my $l = $#{$left->{version}};
  256         458  
798 256         356 my $r = $#{$right->{version}};
  256         400  
799 256 100       437 my $m = $l < $r ? $l : $r;
800 256         513 my $lalpha = $left->is_alpha;
801 256         525 my $ralpha = $right->is_alpha;
802 256         343 my $retval = 0;
803 256         311 my $i = 0;
804 256   100     841 while ( $i <= $m && $retval == 0 ) {
805 612         960 $retval = $left->{version}[$i] <=> $right->{version}[$i];
806 612         1379 $i++;
807             }
808              
809             # possible match except for trailing 0's
810 256 100 100     684 if ( $retval == 0 && $l != $r ) {
811 40 100       119 if ( $l < $r ) {
812 24   66     127 while ( $i <= $r && $retval == 0 ) {
813 24 100       73 if ( $right->{version}[$i] != 0 ) {
814 20         30 $retval = -1; # not a match after all
815             }
816 24         69 $i++;
817             }
818             }
819             else {
820 16   100     114 while ( $i <= $l && $retval == 0 ) {
821 20 100       74 if ( $left->{version}[$i] != 0 ) {
822 12         24 $retval = +1; # not a match after all
823             }
824 20         84 $i++;
825             }
826             }
827             }
828              
829 256         2165 return $retval;
830             }
831              
832             sub vbool {
833 8     8 0 973 my ($self) = @_;
834 8         37 return vcmp($self,$self->new("0"),1);
835             }
836              
837             sub vnoop {
838 28     28 0 5771 require Carp;
839 28         2309 Carp::croak("operation not supported with version object");
840             }
841              
842             sub is_alpha {
843 524     524 0 783 my ($self) = @_;
844 524         1006 return (exists $self->{alpha});
845             }
846              
847             sub qv {
848 24     24 0 1333 my $value = shift;
849 24         47 my $class = $CLASS;
850 24 50       70 if (@_) {
851 24   33     107 $class = ref($value) || $value;
852 24         52 $value = shift;
853             }
854              
855 24         56 $value = _un_vstring($value);
856 24 100       195 $value = 'v'.$value unless $value =~ /(^v|\d+\.\d+\.\d)/;
857 24         82 my $obj = $CLASS->new($value);
858 24         198 return bless $obj, $class;
859             }
860              
861             *declare = \&qv;
862              
863             sub is_qv {
864 36     36 0 72 my ($self) = @_;
865 36         92 return (exists $self->{qv});
866             }
867              
868              
869             sub _verify {
870 916     916   1294 my ($self) = @_;
871 916 50 33     1957 if ( ref($self)
      33        
872 916         3892 && eval { exists $self->{version} }
873             && ref($self->{version}) eq 'ARRAY'
874             ) {
875 916         2343 return 1;
876             }
877             else {
878 0         0 return 0;
879             }
880             }
881              
882             sub _is_non_alphanumeric {
883 184     184   353 my $s = shift;
884 184         429 $s = new charstar $s;
885 184         532 while ($s) {
886 508 100       1003 return 0 if isSPACE($s); # early out
887 504 100 100     846 return 1 unless (isALPHA($s) || isDIGIT($s) || $s =~ /[.-]/);
      100        
888 432         876 $s++;
889             }
890 108         486 return 0;
891             }
892              
893             sub _un_vstring {
894 604     604   919 my $value = shift;
895             # may be a v-string
896 604 100 66     3374 if ( length($value) >= 1 && $value !~ /[,._]/
      100        
897             && _is_non_alphanumeric($value)) {
898 72         112 my $tvalue;
899 72 50       172 if ( $] >= 5.008_001 ) {
    0          
900 72         140 $tvalue = _find_magic_vstring($value);
901 72 100       194 $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         1267 return $value;
912             }
913              
914             sub _find_magic_vstring {
915 72     72   175 my $value = shift;
916 72         117 my $tvalue = '';
917 72         369 require B;
918 72         298 my $sv = B::svref_2object(\$value);
919 72 50       308 my $magic = ref($sv) eq 'B::PVMG' ? $sv->MAGIC : undef;
920 72         214 while ( $magic ) {
921 60 50       188 if ( $magic->TYPE eq 'V' ) {
922 60         146 $tvalue = $magic->PTR;
923 60         507 $tvalue =~ s/^v?(.+)$/v$1/;
924 60         143 last;
925             }
926             else {
927 0         0 $magic = $magic->MOREMAGIC;
928             }
929             }
930 72         166 $tvalue =~ tr/_//d;
931 72         200 return $tvalue;
932             }
933              
934             sub _VERSION {
935 96     96   57558 my ($obj, $req) = @_;
936 96   33     444 my $class = ref($obj) || $obj;
937              
938 1     1   3275 no strict 'refs';
  1         3  
  1         398  
939 96 100 100     391 if ( exists $INC{"$class.pm"} and not %{"$class\::"} and $] >= 5.008) {
  84   66     441  
940             # file but no package
941 4         21 require Carp;
942 4         431 Carp::croak( "$class defines neither package nor VERSION"
943             ."--version check failed");
944             }
945              
946 92         5371 my $version = eval "\$$class\::VERSION";
947 92 100       408 if ( defined $version ) {
948 68 50       182 local $^W if $] <= 5.008;
949 68         228 $version = version::vpp->new($version);
950             }
951              
952 84 100       257 if ( defined $req ) {
953 60 100       150 unless ( defined $version ) {
954 8         55 require Carp;
955 8 50       66 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       30 if ( $ENV{VERSION_DEBUG} ) {
961 0         0 Carp::confess($msg);
962             }
963             else {
964 8         1134 Carp::croak($msg);
965             }
966             }
967              
968 52         127 $req = version::vpp->new($req);
969              
970 52 100       157 if ( $req > $version ) {
971 36         193 require Carp;
972 36 100       101 if ( $req->is_qv ) {
973 8         37 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         88 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       355 return defined $version ? $version->stringify : undef;
990             }
991              
992             1; #this line is important and will help the module return a true value