During 'make', autodoc.pl complains as follows:

  no docs for prescan_version

Does this need correcting?

On Wed, Jan 13, 2010 at 22:27, David Golden <xda...@gmail.com> wrote:
> In perl.git, the branch blead has been updated
>
> <http://perl5.git.perl.org/perl.git/commitdiff/91152fc19d1c59a1213e39f74ac8a80f4a015f5e?hp=32709fdf41543f067562e0dc9944448dd11d2c28>
>
> - Log -----------------------------------------------------------------
> commit 91152fc19d1c59a1213e39f74ac8a80f4a015f5e
> Author: David Golden <dagol...@cpan.org>
> Date:   Wed Jan 13 21:47:30 2010 -0500
>
>    Omnibus strict and lax version parsing
>
>    Authors: John Peacock, David Golden and Zefram
>
>    The goal of this mega-patch is to enforce strict rules for version
>    numbers provided to 'package NAME VERSION' while formalizing the prior,
>    lax rules used for version object creation.  Parsing for use() is
>    unchanged.
>
>    version.pm adds two globals, $STRICT and $LAX, containing regular
>    expressions that define the rules.  There are two additional functions
>    -- version::is_strict and version::is_lax -- that test an argument
>    against these rules.
>
>    However, parsing of strings that might contain version numbers is done
>    in core via the Perl_scan_version function, which may be called during
>    compilation or may be called later when version objects are created by
>    Perl_new_version or Perl_upg_version.
>
>    A new helper function, Perl_prescan_version, has been added to validate
>    a string under either strict or lax rules.  This is used in toke.c for
>    'package NAME VERSION' in strict mode and by Perl_scan_version in lax
>    mode.  It matches the behavior of the verison.pm regular expressions,
>    but does not use them directly.
>
>    A new test file, comp/packagev.t, validates strict and lax behaviors of
>    'package NAME VERSION' and 'version->new(VERSION)' respectively and
>    verifies their behavior against the $STRICT and $LAX regular
>    expressions, as well.  Validating these two implementation should help
>    ensure they each work as intended.
>
>    Other files and tests have been modified as necessary to support these
>    changes.
>
>    There is remaining work to be done in a few areas:
>
>    * documenting all changes in behavior and new functions
>
>    * determining proper treatment of "," as decimal separators in
>      various locales
>
>    * updating diagnostics for new error messages
>
>    * porting changes back to the version.pm distribution on CPAN,
>      including pure-Perl versions
> -----------------------------------------------------------------------
>
> Summary of changes:
>  dist/Safe/Safe.pm          |    2 +
>  dist/XSLoader/t/XSLoader.t |    8 +-
>  embed.fnc                  |    3 +
>  embed.h                    |    4 +
>  global.sym                 |    1 +
>  handy.h                    |   12 ++
>  lib/version.pm             |  113 +++++++++++++++++-
>  lib/version.t              |   40 +++----
>  proto.h                    |   10 ++
>  t/comp/package.t           |   34 +-----
>  t/comp/packagev.t          |  169 ++++++++++++++++++++++++++
>  t/porting/diag.t           |    6 +
>  toke.c                     |   49 ++++++++-
>  universal.c                |    8 +-
>  util.c                     |  279 +++++++++++++++++++++++++++++++++++--------
>  15 files changed, 614 insertions(+), 124 deletions(-)
>  create mode 100644 t/comp/packagev.t
>
> diff --git a/dist/Safe/Safe.pm b/dist/Safe/Safe.pm
> index eb7d68b..476b9fd 100644
> --- a/dist/Safe/Safe.pm
> +++ b/dist/Safe/Safe.pm
> @@ -62,6 +62,8 @@ my $default_share = [qw[
>     &utf8::unicode_to_native
>     $version::VERSION
>     $version::CLASS
> +    $version::STRICT
> +    $version::LAX
>     @version::ISA
>  ], ($] >= 5.008001 && qw[
>     &Regexp::DESTROY
> diff --git a/dist/XSLoader/t/XSLoader.t b/dist/XSLoader/t/XSLoader.t
> index 038986e..211c4d8 100644
> --- a/dist/XSLoader/t/XSLoader.t
> +++ b/dist/XSLoader/t/XSLoader.t
> @@ -30,7 +30,7 @@ my %modules = (
>     'Time::HiRes'=> q| ::can_ok( 'Time::HiRes' => 'usleep'  ) |,  # 5.7.3
>  );
>
> -plan tests => keys(%modules) * 4 + 5;
> +plan tests => keys(%modules) * 3 + 5;
>
>  # Try to load the module
>  use_ok( 'XSLoader' );
> @@ -65,11 +65,9 @@ for my $module (sort keys %modules) {
>     SKIP: {
>         skip "$module not available", 4 if $extensions !~ /\b$module\b/;
>
> -        eval qq{ package $module; XSLoader::load('$module', "qunckkk"); };
> -        like( $@, "/^$module object version \\S+ does not match bootstrap 
> parameter (?:qunckkk|0)/",
> +        eval qq{ package $module; XSLoader::load('$module', "12345678"); };
> +        like( $@, "/^$module object version \\S+ does not match bootstrap 
> parameter (?:12345678|0)/",
>                 "calling XSLoader::load() with a XS module and an incorrect 
> version" );
> -        like( $warnings, "/^\$|^Version string 'qunckkk' contains invalid 
> data; ignoring: 'qunckkk'/",
> -                "in Perl 5.10, DynaLoader warns about the incorrect version 
> string" );
>
>         eval qq{ package $module; XSLoader::load('$module'); };
>         is( $@, '',  "XSLoader::load($module)");
> diff --git a/embed.fnc b/embed.fnc
> index 17089ff..abfa92b 100644
> --- a/embed.fnc
> +++ b/embed.fnc
> @@ -745,6 +745,8 @@ Apa |PERL_SI*|new_stackinfo|I32 stitems|I32 cxitems
>  Ap     |char*  |scan_vstring   |NN const char *s|NN const char *const e \
>                                |NN SV *sv
>  Apd    |const char*    |scan_version   |NN const char *s|NN SV *rv|bool qv
> +Apd    |const char*    |prescan_version        |NN const char *s\
> +       |bool strict|NULLOK const char** errstr|bool *sqv|int 
> *ssaw_period|int *swidth|bool *salpha
>  Apd    |SV*    |new_version    |NN SV *ver
>  Apd    |SV*    |upg_version    |NN SV *ver|bool qv
>  Apd    |bool   |vverify        |NN SV *vs
> @@ -1804,6 +1806,7 @@ sRn       |PTR_TBL_ENT_t *|ptr_table_find|NN PTR_TBL_t 
> *const tbl|NULLOK const void *c
>  s      |void   |check_uni
>  s      |void   |force_next     |I32 type
>  s      |char*  |force_version  |NN char *s|int guessing
> +s      |char*  |force_strict_version   |NN char *s
>  s      |char*  |force_word     |NN char *start|int token|int check_keyword \
>                                |int allow_pack|int allow_tick
>  s      |SV*    |tokeq          |NN SV *sv
> diff --git a/embed.h b/embed.h
> index c949c5c..246106b 100644
> --- a/embed.h
> +++ b/embed.h
> @@ -636,6 +636,7 @@
>  #define new_stackinfo          Perl_new_stackinfo
>  #define scan_vstring           Perl_scan_vstring
>  #define scan_version           Perl_scan_version
> +#define prescan_version                Perl_prescan_version
>  #define new_version            Perl_new_version
>  #define upg_version            Perl_upg_version
>  #define vverify                        Perl_vverify
> @@ -1591,6 +1592,7 @@
>  #define check_uni              S_check_uni
>  #define force_next             S_force_next
>  #define force_version          S_force_version
> +#define force_strict_version   S_force_strict_version
>  #define force_word             S_force_word
>  #define tokeq                  S_tokeq
>  #define readpipe_override      S_readpipe_override
> @@ -3036,6 +3038,7 @@
>  #define new_stackinfo(a,b)     Perl_new_stackinfo(aTHX_ a,b)
>  #define scan_vstring(a,b,c)    Perl_scan_vstring(aTHX_ a,b,c)
>  #define scan_version(a,b,c)    Perl_scan_version(aTHX_ a,b,c)
> +#define prescan_version(a,b,c,d,e,f,g) Perl_prescan_version(aTHX_ 
> a,b,c,d,e,f,g)
>  #define new_version(a)         Perl_new_version(aTHX_ a)
>  #define upg_version(a,b)       Perl_upg_version(aTHX_ a,b)
>  #define vverify(a)             Perl_vverify(aTHX_ a)
> @@ -4000,6 +4003,7 @@
>  #define check_uni()            S_check_uni(aTHX)
>  #define force_next(a)          S_force_next(aTHX_ a)
>  #define force_version(a,b)     S_force_version(aTHX_ a,b)
> +#define force_strict_version(a)        S_force_strict_version(aTHX_ a)
>  #define force_word(a,b,c,d,e)  S_force_word(aTHX_ a,b,c,d,e)
>  #define tokeq(a)               S_tokeq(aTHX_ a)
>  #define readpipe_override()    S_readpipe_override(aTHX)
> diff --git a/global.sym b/global.sym
> index ae6a48f..f0361df 100644
> --- a/global.sym
> +++ b/global.sym
> @@ -376,6 +376,7 @@ Perl_newWHILEOP
>  Perl_new_stackinfo
>  Perl_scan_vstring
>  Perl_scan_version
> +Perl_prescan_version
>  Perl_new_version
>  Perl_upg_version
>  Perl_vverify
> diff --git a/handy.h b/handy.h
> index 63f7fd8..07ab78d 100644
> --- a/handy.h
> +++ b/handy.h
> @@ -656,6 +656,18 @@ US-ASCII (Basic Latin) range are viewed as not having 
> any case.
>  typedef U32 line_t;
>  #define NOLINE ((line_t) 4294967295UL)
>
> +/* Helpful alias for version prescan */
> +#define is_LAX_VERSION(a,b) \
> +       (a != Perl_prescan_version(aTHX_ a, FALSE, b, NULL, NULL, NULL, NULL))
> +
> +#define is_STRICT_VERSION(a,b) \
> +       (a != Perl_prescan_version(aTHX_ a, TRUE, b, NULL, NULL, NULL, NULL))
> +
> +#define BADVERSION(a,b,c) \
> +       if (b) { \
> +           *b = c; \
> +       } \
> +       return a;
>
>  /*
>  =head1 Memory Management
> diff --git a/lib/version.pm b/lib/version.pm
> index 9201a02..424463d 100644
> --- a/lib/version.pm
> +++ b/lib/version.pm
> @@ -4,12 +4,116 @@ package version;
>  use 5.005_04;
>  use strict;
>
> -use vars qw(@ISA $VERSION $CLASS *declare *qv);
> +use vars qw(@ISA $VERSION $CLASS $STRICT $LAX *declare *qv);
>
> -$VERSION = 0.77;
> +$VERSION = 0.81;
>
>  $CLASS = 'version';
>
> +#--------------------------------------------------------------------------#
> +# Version regexp components
> +#--------------------------------------------------------------------------#
> +
> +# Fraction part of a decimal version number.  This is a common part of
> +# both strict and lax decimal versions
> +
> +my $FRACTION_PART = qr/\.[0-9]+/;
> +
> +# First part of either decimal or dotted-decimal strict version number.
> +# Unsigned integer with no leading zeroes (except for zero itself) to
> +# avoid confusion with octal.
> +
> +my $STRICT_INTEGER_PART = qr/0|[1-9][0-9]*/;
> +
> +# First part of either decimal or dotted-decimal lax version number.
> +# Unsigned integer, but allowing leading zeros.  Always interpreted
> +# as decimal.  However, some forms of the resulting syntax give odd
> +# results if used as ordinary Perl expressions, due to how perl treats
> +# octals.  E.g.
> +#   version->new("010" ) == 10
> +#   version->new( 010  ) == 8
> +#   version->new( 010.2) == 82  # "8" . "2"
> +
> +my $LAX_INTEGER_PART = qr/[0-9]+/;
> +
> +# Second and subsequent part of a strict dotted-decimal version number.
> +# Leading zeroes are permitted, and the number is always decimal.
> +# Limited to three digits to avoid overflow when converting to decimal
> +# form and also avoid problematic style with excessive leading zeroes.
> +
> +my $STRICT_DOTTED_DECIMAL_PART = qr/\.[0-9]{1,3}/;
> +
> +# Second and subsequent part of a lax dotted-decimal version number.
> +# Leading zeroes are permitted, and the number is always decimal.  No
> +# limit on the numerical value or number of digits, so there is the
> +# possibility of overflow when converting to decimal form.
> +
> +my $LAX_DOTTED_DECIMAL_PART = qr/\.[0-9]+/;
> +
> +# Alpha suffix part of lax version number syntax.  Acts like a
> +# dotted-decimal part.
> +
> +my $LAX_ALPHA_PART = qr/_[0-9]+/;
> +
> +#--------------------------------------------------------------------------#
> +# Strict version regexp definitions
> +#--------------------------------------------------------------------------#
> +
> +# Strict decimal version number.
> +
> +my $STRICT_DECIMAL_VERSION =
> +    qr/ $STRICT_INTEGER_PART $FRACTION_PART? /x;
> +
> +# Strict dotted-decimal version number.  Must have both leading "v" and
> +# at least three parts, to avoid confusion with decimal syntax.
> +
> +my $STRICT_DOTTED_DECIMAL_VERSION =
> +    qr/ v $STRICT_INTEGER_PART $STRICT_DOTTED_DECIMAL_PART{2,} /x;
> +
> +# Complete strict version number syntax -- should generally be used
> +# anchored: qr/ \A $STRICT \z /x
> +
> +$STRICT =
> +    qr/ $STRICT_DECIMAL_VERSION | $STRICT_DOTTED_DECIMAL_VERSION /x;
> +
> +#--------------------------------------------------------------------------#
> +# Lax version regexp definitions
> +#--------------------------------------------------------------------------#
> +
> +# Lax decimal version number.  Just like the strict one except for
> +# allowing an alpha suffix or allowing a leading or trailing
> +# decimal-point
> +
> +my $LAX_DECIMAL_VERSION =
> +    qr/ $LAX_INTEGER_PART (?: \. | $FRACTION_PART $LAX_ALPHA_PART? )?
> +       |
> +       $FRACTION_PART $LAX_ALPHA_PART?
> +    /x;
> +
> +# Lax dotted-decimal version number.  Distinguished by having either
> +# leading "v" or at least three non-alpha parts.  Alpha part is only
> +# permitted if there are at least two non-alpha parts. Strangely
> +# enough, without the leading "v", Perl takes .1.2 to mean v0.1.2,
> +# so when there is no "v", the leading part is optional
> +
> +my $LAX_DOTTED_DECIMAL_VERSION =
> +    qr/
> +       v $LAX_INTEGER_PART (?: $LAX_DOTTED_DECIMAL_PART+ $LAX_ALPHA_PART? )?
> +       |
> +       $LAX_INTEGER_PART? $LAX_DOTTED_DECIMAL_PART{2,} $LAX_ALPHA_PART?
> +    /x;
> +
> +# Complete lax version number syntax -- should generally be used
> +# anchored: qr/ \A $LAX \z /x
> +#
> +# The string 'undef' is a special case to make for easier handling
> +# of return values from ExtUtils::MM->parse_version
> +
> +$LAX =
> +    qr/ undef | $LAX_DECIMAL_VERSION | $LAX_DOTTED_DECIMAL_VERSION /x;
> +
> +#--------------------------------------------------------------------------#
> +
>  # Preloaded methods go here.
>  sub import {
>     no strict 'refs';
> @@ -33,7 +137,7 @@ sub import {
>            'UNIVERSAL::VERSION' => 1,
>        );
>     }
> -
> +
>     my $callpkg = caller();
>
>     if (exists($args{declare})) {
> @@ -53,4 +157,7 @@ sub import {
>     }
>  }
>
> +sub is_strict  { defined $_[0] && $_[0] =~ qr/ \A $STRICT \z /x }
> +sub is_lax     { defined $_[0] && $_[0] =~ qr/ \A $LAX \z /x }
> +
>  1;
> diff --git a/lib/version.t b/lib/version.t
> index 8067f1a..f44cfea 100644
> --- a/lib/version.t
> +++ b/lib/version.t
> @@ -132,43 +132,32 @@ sub BaseTests {
>
>     # test illegal formats
>     diag "test illegal formats" unless $ENV{PERL_CORE};
> -    eval {my $version = $CLASS->$method("1.2_3_4")};
> +    eval {$version = $CLASS->$method("1.2_3_4")};
>     like($@, qr/multiple underscores/,
>        "Invalid version format (multiple underscores)");
>
> -    eval {my $version = $CLASS->$method("1.2_3.4")};
> +    eval {$version = $CLASS->$method("1.2_3.4")};
>     like($@, qr/underscores before decimal/,
>        "Invalid version format (underscores before decimal)");
>
> -    eval {my $version = $CLASS->$method("1_2")};
> +    eval {$version = $CLASS->$method("1_2")};
>     like($@, qr/alpha without decimal/,
>        "Invalid version format (alpha without decimal)");
>
> -    # for this test, upgrade the warn() to die()
> -    eval {
> -       local $SIG{__WARN__} = sub { die $_[0] };
> -       $version = $CLASS->$method("1.2b3");
> -    };
> -    my $warnregex = "Version string '.+' contains invalid data; ".
> -           "ignoring: '.+'";
> -
> -    like($@, qr/$warnregex/,
> -       "Version string contains invalid data; ignoring");
> +    eval { $version = $CLASS->$method("1.2b3")};
> +    like($@, qr/non-numeric data/,
> +       "Invalid version format (non-numeric data)");
>
>     # from here on out capture the warning and test independently
>     {
> -    $version = $CLASS->$method("99 and 44/100 pure");
> +    eval{$version = $CLASS->$method("99 and 44/100 pure")};
>
> -    like($warning, qr/$warnregex/,
> -       "Version string contains invalid data; ignoring");
> -    is ("$version", "99", '$version eq "99"');
> -    ok ($version->numify == 99.0, '$version->numify == 99.0');
> -    ok ($version->normal eq "v99.0.0", '$version->normal eq v99.0.0');
> +    like($@, qr/non-numeric data/,
> +       "Invalid version format (non-numeric data)");
>
> -    $version = $CLASS->$method("something");
> -    like($warning, qr/$warnregex/,
> -       "Version string contains invalid data; ignoring");
> -    ok (defined $version, 'defined $version');
> +    eval{$version = $CLASS->$method("something")};
> +    like($@, qr/non-numeric data/,
> +       "Invalid version format (non-numeric data)");
>
>     # reset the test object to something reasonable
>     $version = $CLASS->$method("1.2.3");
> @@ -557,9 +546,8 @@ SKIP: {
>        local $SIG{__WARN__} = sub { $warning = $_[0] };
>
>  $DB::single = 1;
> -       my $v = $CLASS->$method('1,7');
> -       unlike($warning, qr"Version string '1,7' contains invalid data",
> -           'Directly test comma as decimal compliance');
> +       my $v = eval { $CLASS->$method('1,7') };
> +#      is( $@, "", 'Directly test comma as decimal compliance');
>
>        my $ver = 1.23;  # has to be floating point number
>        my $orig_loc = setlocale( LC_ALL );
> diff --git a/proto.h b/proto.h
> index 02fdd2d..223086c 100644
> --- a/proto.h
> +++ b/proto.h
> @@ -2383,6 +2383,11 @@ PERL_CALLCONV const char*        
> Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv
>  #define PERL_ARGS_ASSERT_SCAN_VERSION  \
>        assert(s); assert(rv)
>
> +PERL_CALLCONV const char*      Perl_prescan_version(pTHX_ const char *s, 
> bool strict, const char** errstr, bool *sqv, int *ssaw_period, int *swidth, 
> bool *salpha)
> +                       __attribute__nonnull__(pTHX_1);
> +#define PERL_ARGS_ASSERT_PRESCAN_VERSION       \
> +       assert(s)
> +
>  PERL_CALLCONV SV*      Perl_new_version(pTHX_ SV *ver)
>                        __attribute__nonnull__(pTHX_1);
>  #define PERL_ARGS_ASSERT_NEW_VERSION   \
> @@ -5802,6 +5807,11 @@ STATIC char*     S_force_version(pTHX_ char *s, int 
> guessing)
>  #define PERL_ARGS_ASSERT_FORCE_VERSION \
>        assert(s)
>
> +STATIC char*   S_force_strict_version(pTHX_ char *s)
> +                       __attribute__nonnull__(pTHX_1);
> +#define PERL_ARGS_ASSERT_FORCE_STRICT_VERSION  \
> +       assert(s)
> +
>  STATIC char*   S_force_word(pTHX_ char *start, int token, int check_keyword, 
> int allow_pack, int allow_tick)
>                        __attribute__nonnull__(pTHX_1);
>  #define PERL_ARGS_ASSERT_FORCE_WORD    \
> diff --git a/t/comp/package.t b/t/comp/package.t
> index 85fd1a5..fa28868 100644
> --- a/t/comp/package.t
> +++ b/t/comp/package.t
> @@ -1,6 +1,6 @@
>  #!./perl
>
> -print "1..22\n";
> +print "1..14\n";
>
>  $blurfl = 123;
>  $foo = 3;
> @@ -72,35 +72,3 @@ package bug32562;
>  print       __PACKAGE__  eq 'bug32562' ? "ok 13\n" : "not ok 13\n";
>  print eval '__PACKAGE__' eq 'bug32562' ? "ok 14\n" : "not ok 14\n";
>
> -# test: package NAME VERSION
> -
> -my @variations = (
> -  '1.00',
> -  '1.00_01',
> -  'v1.2.3',
> -  'v1.2_3',
> -);
> -
> -my $test_count = 15;
> -
> -for my $v ( @variations ) {
> -  my $ok = eval "package withversion $v; $v eq \$withversion::VERSION";
> -  print $ok ? "ok $test_count\n" : "not ok $test_count\n";
> -  $test_count++;
> -}
> -
> -eval q/package Foo Bar/;
> -$@ =~ /syntax error/ or print "not ";
> -print "ok $test_count\n"; $test_count++;
> -
> -eval q/package Foo 1a/;
> -$@ =~ /syntax error/ or print "not ";
> -print "ok $test_count\n"; $test_count++;
> -
> -eval q/package Foo v/;
> -$@ =~ /syntax error/ or print "not ";
> -print "ok $test_count\n"; $test_count++;
> -
> -eval q/package Foo $foo/;
> -$@ =~ /syntax error/ or print "not ";
> -print "ok $test_count\n"; $test_count++;
> diff --git a/t/comp/packagev.t b/t/comp/packagev.t
> new file mode 100644
> index 0000000..bc99ec4
> --- /dev/null
> +++ b/t/comp/packagev.t
> @@ -0,0 +1,169 @@
> +#!./perl
> +
> +BEGIN {
> +    chdir 't';
> +   �...@inc = '../lib';
> +    require './test.pl';
> +}
> +
> +# XXX remove this later -- dagolden, 2010-01-13
> +# local *STDERR = *STDOUT;
> +
> +my @syntax_cases = (
> +    'package Foo',
> +    'package Bar 1.23',
> +    'package Baz v1.2.3',
> +);
> +
> +my @version_cases = <DATA>;
> +
> +plan tests => 5 * @syntax_cases + 5 * grep { $_ !~ /^#/ } @version_cases;
> +
> +use warnings qw/syntax/;
> +use version;
> +
> +for my $string ( @syntax_cases ) {
> +    eval "$string";
> +    is( $@, '', qq/eval "$string"/ );
> +    eval "$string;";
> +    is( $@, '', qq/eval "$string;"/ );
> +    eval "$string ;";
> +    is( $@, '', qq/eval "$string ;"/ );
> +    eval "{$string}";
> +    is( $@, '', qq/eval "{$string}"/ );
> +    eval "{ $string }";
> +    is( $@, '', qq/eval "{ $string }"/ );
> +}
> +
> +LINE:
> +for my $line (@version_cases) {
> +    chomp $line;
> +    # comments in data section are just diagnostics
> +    if ($line =~ /^#/) {
> +       diag $line;
> +       next LINE;
> +    }
> +
> +    my ($v, $package, $quoted, $bare, $match) = split /\t+/, $line;
> +    my $warning = "";
> +    local $SIG{__WARN__} = sub { $warning .= $_[0] . "\n" };
> +    $match = defined $match ? $match : "";
> +    $match =~ s/\s*\z//; # kill trailing spaces
> +
> +    # First handle the 'package NAME VERSION' case
> +    $withversion::VERSION = undef;
> +    if ($package eq 'fail') {
> +       eval "package withversion $v";
> +       like($@, qr/$match/, "package withversion $v -> syntax error 
> ($match)");
> +       ok(! version::is_strict($v), qq{... and "$v" should also fail STRICT 
> regex});
> +    }
> +    else {
> +       my $ok = eval "package withversion $v; $v eq \$withversion::VERSION";
> +       ok($ok, "package withversion $v")
> +          or diag( $@ ? $@ : "and \$VERSION = $withversion::VERSION");
> +       ok( version::is_strict($v), qq{... and "$v" should pass STRICT 
> regex});
> +    }
> +
> +
> +    # Now check the version->new("V") case
> +    my $ver = undef;
> +    eval qq/\$ver = version->new("$v")/;
> +    if ($quoted eq 'fail') {
> +       like($@, qr/$match/, qq{version->new("$v") -> invalid format 
> ($match)})
> +          or diag( $@ ? $@ : "and \$ver = $ver" );
> +       ok( ! version::is_lax($v), qq{... and "$v" should fail LAX regex});
> +    }
> +    else {
> +       is($@, "", qq{version->new("$v")});
> +       ok( version::is_lax($v), qq{... and "$v" should pass LAX regex});
> +    }
> +
> +    # Now check the version->new(V) case, unless we're skipping it
> +    if ( $bare eq 'na' ) {
> +        pass( "... skipping version->new($v)" );
> +       next LINE;
> +    }
> +    $ver = undef;
> +    eval qq/\$ver = version->new($v)/;
> +    if ($bare eq 'fail') {
> +       like($@, qr/$match/m, qq{... and unquoted version->new($v) has same 
> error})
> +          or diag( $@ ? $@ : "and \$ver = $ver" );
> +    }
> +    else {
> +       is($@, "", qq{... and version->new($v) is ok});
> +    }
> +}
> +
> +
> +# The data is organized in tab delimited format with these columns:
> +#
> +# value                package         version->new    version->new    regex
> +#                              quoted          unquoted
> +#
> +# For each value, it is tested using eval in the following expressions
> +#
> +#      package foo $value;                     # column 2
> +# and
> +#      my $ver = version->new("$value");       # column 3
> +# and
> +#      my $ver = version->new($value);         # column 4
> +#
> +# The second through fourth columns can contain 'pass' or 'fail'.
> +#
> +# For any column with 'pass', the tests makes sure that no warning/error
> +# was thrown.  For any column with 'fail', the tests make sure that the
> +# error thrown matches the regex in the last column.  The unquoted column
> +# may also have 'na' indicating that it's pointless to test as behavior
> +# is subject to the perl parser before a stringifiable value is available
> +# to version->new
> +#
> +# If all columns are marked 'pass', the regex column is left empty.
> +#
> +# there are multiple ways that underscores can fail depending on strict
> +# vs lax format so these test do not distinguish between them
> +#
> +# If the DATA line begins with a # mark, it is used as a diag comment
> +__DATA__
> +1.00           pass    pass    pass
> +1.00001                pass    pass    pass
> +0.123          pass    pass    pass
> +12.345         pass    pass    pass
> +42             pass    pass    pass
> +0              pass    pass    pass
> +0.0            pass    pass    pass
> +v1.2.3         pass    pass    pass
> +v1.2.3.4       pass    pass    pass
> +v0.1.2         pass    pass    pass
> +v0.0.0         pass    pass    pass
> +01             fail    pass    pass    no leading zeros
> +01.0203                fail    pass    pass    no leading zeros
> +v01            fail    pass    pass    no leading zeros
> +v01.02.03      fail    pass    pass    no leading zeros
> +.1             fail    pass    pass    0 before decimal required
> +.1.2           fail    pass    pass    0 before decimal required
> +1.             fail    pass    pass    fractional part required
> +1.a            fail    fail    na      fractional part required
> +1._            fail    fail    na      fractional part required
> +1.02_03                fail    pass    pass    underscore
> +v1.2_3         fail    pass    pass    underscore
> +v1.02_03       fail    pass    pass    underscore
> +v1.2_3_4       fail    fail    fail    underscore
> +v1.2_3.4       fail    fail    fail    underscore
> +1.2_3.4                fail    fail    fail    underscore
> +0_             fail    fail    na      underscore
> +1_             fail    fail    na      underscore
> +1_.            fail    fail    na      underscore
> +1.1_           fail    fail    na      underscore
> +1.02_03_04     fail    fail    na      underscore
> +1.2.3          fail    pass    pass    dotted-decimal versions must begin 
> with 'v'
> +v1.2           fail    pass    pass    dotted-decimal versions require at 
> least three parts
> +v0             fail    pass    pass    dotted-decimal versions require at 
> least three parts
> +v1             fail    pass    pass    dotted-decimal versions require at 
> least three parts
> +v.1.2.3                fail    fail    na      dotted-decimal versions 
> require at least three parts
> +v              fail    fail    na      dotted-decimal versions require at 
> least three parts
> +v1.2345.6      fail    pass    pass    maximum 3 digits between decimals
> +undef          fail    pass    pass    non-numeric data
> +1a             fail    fail    na      non-numeric data
> +1.2a3          fail    fail    na      non-numeric data
> +bar            fail    fail    na      non-numeric data
> +_              fail    fail    na      non-numeric data
> diff --git a/t/porting/diag.t b/t/porting/diag.t
> index 65e1958..06f9849 100644
> --- a/t/porting/diag.t
> +++ b/t/porting/diag.t
> @@ -273,6 +273,12 @@ Invalid type '%c' in pack
>  Invalid type '%c' in %s
>  Invalid type '%c' in unpack
>  Invalid type ',' in %s
> +Invalid strict version format (0 before decimal required)
> +Invalid strict version format (no leading zeros)
> +Invalid strict version format (no underscores)
> +Invalid strict version format (v1.2.3 required)
> +Invalid strict version format (version required)
> +Invalid strict version format (1.[0-9] required)
>  Invalid version format (alpha without decimal)
>  Invalid version format (misplaced _ in number)
>  Invalid version object
> diff --git a/toke.c b/toke.c
> index deae6a5..2b98ada 100644
> --- a/toke.c
> +++ b/toke.c
> @@ -2130,6 +2130,53 @@ S_force_version(pTHX_ char *s, int guessing)
>  }
>
>  /*
> + * S_force_strict_version
> + * Forces the next token to be a version number using strict syntax rules.
> + */
> +
> +STATIC char *
> +S_force_strict_version(pTHX_ char *s)
> +{
> +    dVAR;
> +    OP *version = NULL;
> +#ifdef PERL_MAD
> +    I32 startoff = s - SvPVX(PL_linestr);
> +#endif
> +    const char *errstr = NULL;
> +
> +    PERL_ARGS_ASSERT_FORCE_STRICT_VERSION;
> +
> +    while (isSPACE(*s)) /* leading whitespace */
> +       s++;
> +
> +    if (is_STRICT_VERSION(s,&errstr)) {
> +       SV *ver = newSV(0);
> +       s = (char *)scan_version(s, ver, 0);
> +       version = newSVOP(OP_CONST, 0, ver);
> +    }
> +    else if ( (*s != ';' && *s != '}' ) && (s = SKIPSPACE1(s), (*s != ';' && 
> *s !='}' ))) {
> +       PL_bufptr = s;
> +       if (errstr)
> +           yyerror(errstr); /* version required */
> +       return s;
> +    }
> +
> +#ifdef PERL_MAD
> +    if (PL_madskills && !version) {
> +       sv_free(PL_nextwhite);  /* let next token collect whitespace */
> +       PL_nextwhite = 0;
> +       s = SvPVX(PL_linestr) + startoff;
> +    }
> +#endif
> +    /* NOTE: The parser sees the package name and the VERSION swapped */
> +    start_force(PL_curforce);
> +    NEXTVAL_NEXTTOKE.opval = version;
> +    force_next(WORD);
> +
> +    return s;
> +}
> +
> +/*
>  * S_tokeq
>  * Tokenize a quoted string passed in as an SV.  It finds the next
>  * chunk, up to end of string or a backslash.  It may make a new
> @@ -6961,7 +7008,7 @@ Perl_yylex(pTHX)
>
>        case KEY_package:
>            s = force_word(s,WORD,FALSE,TRUE,FALSE);
> -           s = force_version(s, FALSE);
> +           s = force_strict_version(s);
>            OPERATOR(PACKAGE);
>
>        case KEY_pipe:
> diff --git a/universal.c b/universal.c
> index 3a91c5c..5a2cddb 100644
> --- a/universal.c
> +++ b/universal.c
> @@ -546,10 +546,10 @@ XS(XS_version_new)
>                ? HvNAME(SvSTASH(SvRV(ST(0))))
>                : (char *)SvPV_nolen(ST(0));
>
> -       if ( items == 1 || vs == &PL_sv_undef ) { /* no param or explicit 
> undef */
> +       if ( items == 1 || ! SvOK(vs) ) { /* no param or explicit undef */
>            /* create empty object */
>            vs = sv_newmortal();
> -           sv_setpvs(vs,"");
> +           sv_setpvs(vs, "undef");
>        }
>        else if ( items == 3 ) {
>            vs = sv_newmortal();
> @@ -659,7 +659,7 @@ XS(XS_version_vcmp)
>
>               if ( ! sv_derived_from(robj, "version") )
>               {
> -                   robj = new_version(robj);
> +                   robj = new_version(SvOK(robj) ? robj : newSVpvs("undef"));
>               }
>               rvs = SvRV(robj);
>
> @@ -743,7 +743,7 @@ XS(XS_version_qv)
>        SV * ver = ST(0);
>        SV * rv;
>        const char * classname = "";
> -       if ( items == 2 && (ST(1)) != &PL_sv_undef ) {
> +       if ( items == 2 && SvOK(ST(1)) ) {
>            /* getting called as object or class method */
>            ver = ST(1);
>            classname =
> diff --git a/util.c b/util.c
> index 70f5a26..9b11ada 100644
> --- a/util.c
> +++ b/util.c
> @@ -4181,6 +4181,205 @@ Perl_getcwd_sv(pTHX_ register SV *sv)
>  }
>
>  #define VERSION_MAX 0x7FFFFFFF
> +
> +const char *
> +Perl_prescan_version(pTHX_ const char *s, bool strict,
> +                    const char **errstr,
> +                    bool *sqv, int *ssaw_decimal, int *swidth, bool *salpha) 
> {
> +    bool qv = (sqv ? *sqv : FALSE);
> +    int width = 3;
> +    int saw_decimal = 0;
> +    bool alpha = FALSE;
> +    const char *d = s;
> +
> +    PERL_ARGS_ASSERT_PRESCAN_VERSION;
> +
> +    if (qv && isDIGIT(*d))
> +       goto dotted_decimal_version;
> +
> +    if (*d == 'v') { /* explicit v-string */
> +       d++;
> +       if (isDIGIT(*d)) {
> +           qv = TRUE;
> +       }
> +       else { /* degenerate v-string */
> +           /* requires v1.2.3 */
> +           BADVERSION(s,errstr,"Invalid version format (dotted-decimal 
> versions require at least three parts)");
> +       }
> +
> +dotted_decimal_version:
> +       if (strict && d[0] == '0' && isDIGIT(d[1])) {
> +           /* no leading zeros allowed */
> +           BADVERSION(s,errstr,"Invalid version format (no leading zeros)");
> +       }
> +
> +       while (isDIGIT(*d))     /* integer part */
> +           d++;
> +
> +       if (*d == '.')
> +       {
> +           saw_decimal++;
> +           d++;                /* decimal point */
> +       }
> +       else
> +       {
> +           if (strict) {
> +               /* require v1.2.3 */
> +               BADVERSION(s,errstr,"Invalid version format (dotted-decimal 
> versions require at least three parts)");
> +           }
> +           else {
> +               goto version_prescan_finish;
> +           }
> +       }
> +
> +       {
> +           int i = 0;
> +           int j = 0;
> +           while (isDIGIT(*d)) {       /* just keep reading */
> +               i++;
> +               while (isDIGIT(*d)) {
> +                   d++; j++;
> +                   /* maximum 3 digits between decimal */
> +                   if (strict && j > 3) {
> +                       BADVERSION(s,errstr,"Invalid version format (maximum 
> 3 digits between decimals)");
> +                   }
> +               }
> +               if (*d == '_') {
> +                   if (strict) {
> +                       BADVERSION(s,errstr,"Invalid version format (no 
> underscores)");
> +                   }
> +                   if ( alpha ) {
> +                       BADVERSION(s,errstr,"Invalid version format (multiple 
> underscores)");
> +                   }
> +                   d++;
> +                   alpha = TRUE;
> +               }
> +               else if (*d == '.') {
> +                   if (alpha) {
> +                       BADVERSION(s,errstr,"Invalid version format 
> (underscores before decimal)");
> +                   }
> +                   saw_decimal++;
> +                   d++;
> +               }
> +               else if (!isDIGIT(*d)) {
> +                   break;
> +               }
> +               j = 0;
> +           }
> +
> +           if (strict && i < 2) {
> +               /* requires v1.2.3 */
> +               BADVERSION(s,errstr,"Invalid version format (dotted-decimal 
> versions require at least three parts)");
> +           }
> +       }
> +    }                                  /* end if dotted-decimal */
> +    else
> +    {                                  /* decimal versions */
> +       /* special strict case for leading '.' or '0' */
> +       if (strict) {
> +           if (*d == '.') {
> +               BADVERSION(s,errstr,"Invalid version format (0 before decimal 
> required)");
> +           }
> +           if (*d == '0' && isDIGIT(d[1])) {
> +               BADVERSION(s,errstr,"Invalid version format (no leading 
> zeros)");
> +           }
> +       }
> +
> +       /* consume all of the integer part */
> +       while (isDIGIT(*d))
> +           d++;
> +
> +       /* look for a fractional part */
> +       if (*d == '.') {
> +           /* we found it, so consume it */
> +           saw_decimal++;
> +           d++;
> +       }
> +       else if (!*d || *d == ';' || isSPACE(*d) || *d == '}') {
> +           if ( d == s ) {
> +               /* found nothing */
> +               BADVERSION(s,errstr,"Invalid version format (version 
> required)");
> +           }
> +           /* found just an integer */
> +           goto version_prescan_finish;
> +       }
> +       else if ( d == s ) {
> +           /* didn't find either integer or period */
> +           BADVERSION(s,errstr,"Invalid version format (non-numeric data)");
> +       }
> +       else if (*d == '_') {
> +           /* underscore can't come after integer part */
> +           if (strict) {
> +               BADVERSION(s,errstr,"Invalid version format (no 
> underscores)");
> +           }
> +           else if (isDIGIT(d[1])) {
> +               BADVERSION(s,errstr,"Invalid version format (alpha without 
> decimal)");
> +           }
> +           else {
> +               BADVERSION(s,errstr,"Invalid version format (misplaced 
> underscore)");
> +           }
> +       }
> +       else {
> +           /* anything else after integer part is just invalid data */
> +           BADVERSION(s,errstr,"Invalid version format (non-numeric data)");
> +       }
> +
> +       /* scan the fractional part after the decimal point*/
> +
> +       if (!isDIGIT(*d) && (strict || ! (!*d || *d == ';' || isSPACE(*d) || 
> *d == '}') )) {
> +               /* strict or lax-but-not-the-end */
> +               BADVERSION(s,errstr,"Invalid version format (fractional part 
> required)");
> +       }
> +
> +       while (isDIGIT(*d)) {
> +           d++;
> +           if (*d == '.' && isDIGIT(d[-1])) {
> +               if (alpha) {
> +                   BADVERSION(s,errstr,"Invalid version format (underscores 
> before decimal)");
> +               }
> +               if (strict) {
> +                   BADVERSION(s,errstr,"Invalid version format 
> (dotted-decimal versions must begin with 'v')");
> +               }
> +               d = (char *)s;          /* start all over again */
> +               qv = TRUE;
> +               goto dotted_decimal_version;
> +           }
> +           if (*d == '_') {
> +               if (strict) {
> +                   BADVERSION(s,errstr,"Invalid version format (no 
> underscores)");
> +               }
> +               if ( alpha ) {
> +                   BADVERSION(s,errstr,"Invalid version format (multiple 
> underscores)");
> +               }
> +               if ( ! isDIGIT(d[1]) ) {
> +                   BADVERSION(s,errstr,"Invalid version format (misplaced 
> underscore)");
> +               }
> +               d++;
> +               alpha = TRUE;
> +           }
> +       }
> +    }
> +
> +version_prescan_finish:
> +    while (isSPACE(*d))
> +       d++;
> +
> +    if (!isDIGIT(*d) && (! (!*d || *d == ';' || *d == '}') )) {
> +       /* trailing non-numeric data */
> +       BADVERSION(s,errstr,"Invalid version format (non-numeric data)");
> +    }
> +
> +    if (sqv)
> +       *sqv = qv;
> +    if (swidth)
> +       *swidth = width;
> +    if (ssaw_decimal)
> +       *ssaw_decimal = saw_decimal;
> +    if (salpha)
> +       *salpha = alpha;
> +    return d;
> +}
> +
>  /*
>  =for apidoc scan_version
>
> @@ -4209,9 +4408,10 @@ Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
>     const char *start;
>     const char *pos;
>     const char *last;
> -    int saw_period = 0;
> -    int alpha = 0;
> +    const char *errstr = NULL;
> +    int saw_decimal = 0;
>     int width = 3;
> +    bool alpha = FALSE;
>     bool vinf = FALSE;
>     AV * const av = newAV();
>     SV * const hv = newSVrv(rv, "version"); /* create an SV and upgrade the 
> RV */
> @@ -4220,54 +4420,24 @@ Perl_scan_version(pTHX_ const char *s, SV *rv, bool 
> qv)
>
>     (void)sv_upgrade(hv, SVt_PVHV); /* needs to be an HV type */
>
> +#ifndef NODEFAULT_SHAREKEYS
> +    HvSHAREKEYS_on(hv);         /* key-sharing on by default */
> +#endif
> +
>     while (isSPACE(*s)) /* leading whitespace is OK */
>        s++;
>
> -    start = last = s;
> -
> -    if (*s == 'v') {
> -       s++;  /* get past 'v' */
> -       qv = 1; /* force quoted version processing */
> -    }
> -
> -    pos = s;
> -
> -    /* pre-scan the input string to check for decimals/underbars */
> -    while ( *pos == '.' || *pos == '_' || *pos == ',' || isDIGIT(*pos) )
> -    {
> -       if ( *pos == '.' )
> -       {
> -           if ( alpha )
> -               Perl_croak(aTHX_ "Invalid version format (underscores before 
> decimal)");
> -           saw_period++ ;
> -           last = pos;
> +    last = prescan_version(s, FALSE, &errstr, &qv, &saw_decimal, &width, 
> &alpha);
> +    if (errstr) {
> +       /* "undef" is a special case and not an error */
> +       if ( ! ( *s == 'u' && strEQ(s,"undef")) ) {
> +           Perl_croak(aTHX_ "%s", errstr);
>        }
> -       else if ( *pos == '_' )
> -       {
> -           if ( alpha )
> -               Perl_croak(aTHX_ "Invalid version format (multiple 
> underscores)");
> -           alpha = 1;
> -           width = pos - last - 1; /* natural width of sub-version */
> -       }
> -       else if ( *pos == ',' && isDIGIT(pos[1]) )
> -       {
> -           saw_period++ ;
> -           last = pos;
> -       }
> -
> -       pos++;
>     }
>
> -    if ( alpha && !saw_period )
> -       Perl_croak(aTHX_ "Invalid version format (alpha without decimal)");
> -
> -    if ( alpha && saw_period && width == 0 )
> -       Perl_croak(aTHX_ "Invalid version format (misplaced _ in number)");
> -
> -    if ( saw_period > 1 )
> -       qv = 1; /* force quoted version processing */
> -
> -    last = pos;
> +    start = s;
> +    if (*s == 'v')
> +       s++;
>     pos = s;
>
>     if ( qv )
> @@ -4294,7 +4464,7 @@ Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
>                 * point of a version originally created with a bare
>                 * floating point number, i.e. not quoted in any way
>                 */
> -               if ( !qv && s > start && saw_period == 1 ) {
> +               if ( !qv && s > start && saw_decimal == 1 ) {
>                    mult *= 100;
>                    while ( s < end ) {
>                        orev = rev;
> @@ -4384,7 +4554,7 @@ Perl_scan_version(pTHX_ const char *s, SV *rv, bool qv)
>     }
>     else if ( s > start ) {
>        SV * orig = newSVpvn(start,s-start);
> -       if ( qv && saw_period == 1 && *start != 'v' ) {
> +       if ( qv && saw_decimal == 1 && *start != 'v' ) {
>            /* need to insert a v to be consistent */
>            sv_insert(orig, 0, 0, "v", 1);
>        }
> @@ -4433,6 +4603,9 @@ Perl_new_version(pTHX_ SV *ver)
>        /* This will get reblessed later if a derived class*/
>        SV * const hv = newSVrv(rv, "version");
>        (void)sv_upgrade(hv, SVt_PVHV); /* needs to be an HV type */
> +#ifndef NODEFAULT_SHAREKEYS
> +       HvSHAREKEYS_on(hv);         /* key-sharing on by default */
> +#endif
>
>        if ( SvROK(ver) )
>            ver = SvRV(ver);
> @@ -4475,7 +4648,7 @@ Perl_new_version(pTHX_ SV *ver)
>            char * const version = savepvn( (const char*)mg->mg_ptr, len);
>            sv_setpvn(rv,version,len);
>            /* this is for consistency with the pure Perl class */
> -           if ( *version != 'v' )
> +           if ( isDIGIT(*version) )
>                sv_insert(rv, 0, 0, "v", 1);
>            Safefree(version);
>        }
> @@ -4530,7 +4703,7 @@ Perl_upg_version(pTHX_ SV *ver, bool qv)
>  #ifdef SvVOK
>     else if ( (mg = SvVSTRING_mg(ver)) ) { /* already a v-string */
>        version = savepvn( (const char*)mg->mg_ptr,mg->mg_len );
> -       qv = 1;
> +       qv = TRUE;
>     }
>  #endif
>     else /* must be a string or something like a string */
> @@ -4540,12 +4713,14 @@ Perl_upg_version(pTHX_ SV *ver, bool qv)
>  #ifndef SvVOK
>  #  if PERL_VERSION > 5
>        /* This will only be executed for 5.6.0 - 5.8.0 inclusive */
> -       if ( len == 3 && !instr(version,".") && !instr(version,"_") ) {
> +       if ( len >= 3 && !instr(version,".") && !instr(version,"_")
> +           && !(*version == 'u' && strEQ(version, "undef"))
> +           && (*version < '0' || *version > '9') ) {
>            /* may be a v-string */
>            SV * const nsv = sv_newmortal();
>            const char *nver;
>            const char *pos;
> -           int saw_period = 0;
> +           int saw_decimal = 0;
>            sv_setpvf(nsv,"v%vd",ver);
>            pos = nver = savepv(SvPV_nolen(nsv));
>
> @@ -4553,12 +4728,12 @@ Perl_upg_version(pTHX_ SV *ver, bool qv)
>            pos++; /* skip the leading 'v' */
>            while ( *pos == '.' || isDIGIT(*pos) ) {
>                if ( *pos == '.' )
> -                   saw_period++ ;
> +                   saw_decimal++ ;
>                pos++;
>            }
>
>            /* is definitely a v-string */
> -           if ( saw_period == 2 ) {
> +           if ( saw_decimal >= 2 ) {
>                Safefree(version);
>                version = nver;
>            }
>
> --
> Perl5 Master Repository
>

Reply via email to