PageRenderTime 23ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ExtUtils/MakeMaker/version/vpp.pm

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