Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package perl-Text-CSV for openSUSE:Factory 
checked in at 2023-09-13 20:43:28
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/perl-Text-CSV (Old)
 and      /work/SRC/openSUSE:Factory/.perl-Text-CSV.new.1766 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "perl-Text-CSV"

Wed Sep 13 20:43:28 2023 rev:27 rq:1110646 version:2.30.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/perl-Text-CSV/perl-Text-CSV.changes      
2022-08-17 18:22:54.451148287 +0200
+++ /work/SRC/openSUSE:Factory/.perl-Text-CSV.new.1766/perl-Text-CSV.changes    
2023-09-13 20:43:44.168314569 +0200
@@ -1,0 +2,9 @@
+Sat Aug 12 03:09:28 UTC 2023 - Tina Müller <timueller+p...@suse.de>
+
+- updated to 2.03
+   see /usr/share/doc/packages/perl-Text-CSV/Changes
+
+  2.03  2023-08-11
+      - Imported tests/fixes from Text::CSV_XS 1.51
+
+-------------------------------------------------------------------

Old:
----
  Text-CSV-2.02.tar.gz

New:
----
  Text-CSV-2.03.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ perl-Text-CSV.spec ++++++
--- /var/tmp/diff_new_pack.suzTXv/_old  2023-09-13 20:43:45.284354246 +0200
+++ /var/tmp/diff_new_pack.suzTXv/_new  2023-09-13 20:43:45.284354246 +0200
@@ -1,7 +1,7 @@
 #
 # spec file for package perl-Text-CSV
 #
-# Copyright (c) 2022 SUSE LLC
+# Copyright (c) 2023 SUSE LLC
 #
 # All modifications and additions to the file contributed by third parties
 # remain the property of their copyright owners, unless otherwise agreed
@@ -18,19 +18,26 @@
 
 %define cpan_name Text-CSV
 Name:           perl-Text-CSV
-Version:        2.02
+Version:        2.30.0
 Release:        0
+%define cpan_version 2.03
 License:        Artistic-1.0 OR GPL-1.0-or-later
 Summary:        Comma-separated values manipulator (using XS or PurePerl)
 URL:            https://metacpan.org/release/%{cpan_name}
-Source0:        
https://cpan.metacpan.org/authors/id/I/IS/ISHIGAKI/%{cpan_name}-%{version}.tar.gz
+Source0:        
https://cpan.metacpan.org/authors/id/I/IS/ISHIGAKI/%{cpan_name}-%{cpan_version}.tar.gz
 Source1:        cpanspec.yml
 BuildArch:      noarch
 BuildRequires:  perl
 BuildRequires:  perl-macros
 BuildRequires:  perl(Test::More) >= 0.71
+BuildRequires:  perl(Text::CSV_XS) >= 1.510.0
 Requires:       perl(Test::More) >= 0.71
-Recommends:     perl(Text::CSV_XS) >= 1.48
+Requires:       perl(Text::CSV_XS) >= 1.510.0
+Provides:       perl(Text::CSV) = 2.30.0
+Provides:       perl(Text::CSV::ErrorDiag)
+Provides:       perl(Text::CSV_PP) = 2.30.0
+%define         __perllib_provides /bin/true
+Recommends:     perl(Text::CSV_XS) >= 1.510.0
 %{perl_requires}
 
 %description
@@ -41,7 +48,7 @@
 Text::CSV_PP, which is bundled in the same distribution as this module.
 
 %prep
-%autosetup  -n %{cpan_name}-%{version}
+%autosetup  -n %{cpan_name}-%{cpan_version}
 
 %build
 perl Makefile.PL INSTALLDIRS=vendor

++++++ Text-CSV-2.02.tar.gz -> Text-CSV-2.03.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/Changes new/Text-CSV-2.03/Changes
--- old/Text-CSV-2.02/Changes   2022-08-07 22:20:39.000000000 +0200
+++ new/Text-CSV-2.03/Changes   2023-08-11 09:07:51.000000000 +0200
@@ -1,5 +1,8 @@
 Revision history for Perl extension Text::CSV.
 
+2.03  2023-08-11
+    - Imported tests/fixes from Text::CSV_XS 1.51
+
 2.02  2022-08-08
     - Imported tests/fixes from Text::CSV_XS 1.48
     - Fixed a case where csv function is called as a method (GH#46)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/MANIFEST new/Text-CSV-2.03/MANIFEST
--- old/Text-CSV-2.02/MANIFEST  2022-08-07 22:22:06.000000000 +0200
+++ new/Text-CSV-2.03/MANIFEST  2023-08-11 09:10:31.000000000 +0200
@@ -11,6 +11,7 @@
 t/10_base.t
 t/12_acc.t
 t/15_flags.t
+t/16_import.t
 t/20_file.t
 t/21_lexicalio.t
 t/22_scalario.t
@@ -26,6 +27,8 @@
 t/60_samples.t
 t/65_allow.t
 t/66_formula.t
+t/67_emptrow.t
+t/68_header.t
 t/70_rt.t
 t/71_pp.t
 t/75_hashref.t
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/META.json new/Text-CSV-2.03/META.json
--- old/Text-CSV-2.02/META.json 2022-08-07 22:22:06.000000000 +0200
+++ new/Text-CSV-2.03/META.json 2023-08-11 09:10:31.000000000 +0200
@@ -4,7 +4,7 @@
       "Makamaka Hannyaharamitu, E<lt>makamaka[at]cpan.orgE<gt>"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter 
version 2.150010",
+   "generated_by" : "ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter 
version 2.150010",
    "license" : [
       "perl_5"
    ],
@@ -32,12 +32,13 @@
       },
       "runtime" : {
          "recommends" : {
-            "Text::CSV_XS" : "1.48"
+            "Text::CSV_XS" : "1.51"
          },
          "requires" : {
             "IO::Handle" : "0",
             "Test::Harness" : "0",
             "Test::More" : "0.71",
+            "Text::CSV_XS" : "1.51",
             "perl" : "5.006001"
          }
       }
@@ -54,6 +55,6 @@
          "url" : "https://github.com/makamaka/Text-CSV";
       }
    },
-   "version" : "2.02",
-   "x_serialization_backend" : "JSON::PP version 4.05"
+   "version" : "2.03",
+   "x_serialization_backend" : "JSON::PP version 4.16"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/META.yml new/Text-CSV-2.03/META.yml
--- old/Text-CSV-2.02/META.yml  2022-08-07 22:22:06.000000000 +0200
+++ new/Text-CSV-2.03/META.yml  2023-08-11 09:10:31.000000000 +0200
@@ -7,7 +7,7 @@
 configure_requires:
   ExtUtils::MakeMaker: '0'
 dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 7.34, CPAN::Meta::Converter version 
2.150010'
+generated_by: 'ExtUtils::MakeMaker version 7.70, CPAN::Meta::Converter version 
2.150010'
 license: perl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -18,15 +18,16 @@
     - t
     - inc
 recommends:
-  Text::CSV_XS: '1.48'
+  Text::CSV_XS: '1.51'
 requires:
   IO::Handle: '0'
   Test::Harness: '0'
   Test::More: '0.71'
+  Text::CSV_XS: '1.51'
   perl: '5.006001'
 resources:
   bugtracker: https://github.com/makamaka/Text-CSV/issues
   license: http://dev.perl.org/licenses/
   repository: https://github.com/makamaka/Text-CSV
-version: '2.02'
+version: '2.03'
 x_serialization_backend: 'CPAN::Meta::YAML version 0.018'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/lib/Text/CSV.pm 
new/Text-CSV-2.03/lib/Text/CSV.pm
--- old/Text-CSV-2.02/lib/Text/CSV.pm   2022-08-07 22:20:44.000000000 +0200
+++ new/Text-CSV-2.03/lib/Text/CSV.pm   2023-08-11 09:09:36.000000000 +0200
@@ -8,14 +8,14 @@
 @ISA = qw( Exporter );
 
 BEGIN {
-    $VERSION = '2.02';
+    $VERSION = '2.03';
     $DEBUG   = 0;
 }
 
 # if use CSV_XS, requires version
 my $Module_XS  = 'Text::CSV_XS';
 my $Module_PP  = 'Text::CSV_PP';
-my $XS_Version = '1.48';
+my $XS_Version = '1.51';
 
 my $Is_Dynamic = 0;
 
@@ -174,7 +174,7 @@
                 headers => "auto");   # as array of hash
 
  # Write array of arrays as csv file
- csv (in => $aoa, out => "file.csv", sep_char=> ";");
+ csv (in => $aoa, out => "file.csv", sep_char => ";");
 
  # Only show lines where "code" is odd
  csv (in => "data.csv", filter => { code => sub { $_ % 2 }});
@@ -482,14 +482,82 @@
 =head3 skip_empty_rows
 
  my $csv = Text::CSV->new ({ skip_empty_rows => 1 });
-         $csv->skip_empty_rows (0);
+         $csv->skip_empty_rows ("eof");
  my $f = $csv->skip_empty_rows;
 
-If this attribute is set to C<1>,  any row that has an  L</eol> immediately
-following the start of line will be skipped.  Default behavior is to return
-one single empty field.
+This attribute defines the behavior for empty rows:  an L</eol> immediately
+following the start of line. Default behavior is to return one single empty
+field.
+
+This attribute is only used in parsing.  This attribute is ineffective when
+using L</parse> and L</fields>.
+
+Possible values for this attribute are
+
+=over 2
+
+=item 0 | undef
+
+ my $csv = Text::CSV->new ({ skip_empty_rows => 0 });
+ $csv->skip_empty_rows (undef);
+
+No special action is taken. The result will be one single empty field.
+
+=item 1 | "skip"
+
+ my $csv = Text::CSV->new ({ skip_empty_rows => 1 });
+ $csv->skip_empty_rows ("skip");
+
+The row will be skipped.
+
+=item 2 | "eof" | "stop"
+
+ my $csv = Text::CSV->new ({ skip_empty_rows => 2 });
+ $csv->skip_empty_rows ("eof");
+
+The parsing will stop as if an L</eof> was detected.
+
+=item 3 | "die"
+
+ my $csv = Text::CSV->new ({ skip_empty_rows => 3 });
+ $csv->skip_empty_rows ("die");
 
-This attribute is only used in parsing.
+The parsing will stop.  The internal error code will be set to 2015 and the
+parser will C<die>.
+
+=item 4 | "croak"
+
+ my $csv = Text::CSV->new ({ skip_empty_rows => 4 });
+ $csv->skip_empty_rows ("croak");
+
+The parsing will stop.  The internal error code will be set to 2015 and the
+parser will C<croak>.
+
+=item 5 | "error"
+
+ my $csv = Text::CSV->new ({ skip_empty_rows => 5 });
+ $csv->skip_empty_rows ("error");
+
+The parsing will fail.  The internal error code will be set to 2015.
+
+=item callback
+
+ my $csv = Text::CSV->new ({ skip_empty_rows => sub { [] } });
+ $csv->skip_empty_rows (sub { [ 42, $., undef, "empty" ] });
+
+The callback is invoked and its result used instead.  If you want the parse
+to stop after the callback, make sure to return a false value.
+
+The returned value from the callback should be an array-ref. Any other type
+will cause the parse to stop, so these are equivalent in behavior:
+
+ csv (in => $fh, skip_empty_rows => "stop");
+ csv (in => $fh. skip_empty_rows => sub { 0; });
+
+=back
+
+Without arguments, the current value is returned: C<0>, C<1>, C<eof>, C<die>,
+C<croak> or the callback.
 
 =head3 formula_handling
 
@@ -1107,7 +1175,8 @@
 This will return a reference to a list of L<getline ($fh)|/getline> results.
 In this call, C<keep_meta_info> is disabled.  If C<$offset> is negative, as
 with C<splice>, only the last  C<abs ($offset)> records of C<$fh> are taken
-into consideration.
+into consideration. Parameters C<$offset> and C<$length> are expected to be
+an integers. Non-integer values are interpreted as integer without check.
 
 Given a CSV file with 10 lines:
 
@@ -2015,6 +2084,8 @@
 
  my $aoa = csv (in => $fh, headers => "skip");
 
+C<skip> is invalid/ignored in combinations with L<C<detect_bom>|/detect_bom>.
+
 =item auto
 
 If C<auto> is used, the first line of the C<CSV> source will be read as the
@@ -2278,7 +2349,9 @@
 If C<sep_set> is set, the method L</header> is invoked on the opened stream
 to detect and set L<C<sep_char>|/sep_char> with the given set.
 
-C<sep_set> can be abbreviated to C<seps>.
+C<sep_set> can be abbreviated to C<seps>. If neither C<sep_set> not C<seps>
+is given, but C<sep> is defined, C<sep_set> defaults to C<[ sep ]>. This is
+only supported for perl version 5.10 and up.
 
 Note that as the  L</header> method is invoked,  its default is to also set
 the headers.
@@ -2860,6 +2933,11 @@
 Inconsistent number of fields under strict parsing.
 
 =item *
+2015 "ERW - Empty row"
+
+An empty row was not allowed.
+
+=item *
 2021 "EIQ - NL char inside quotes, binary off"
 
 Sequences like C<1,"foo\nbar",22,1> are allowed only when the binary option
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/lib/Text/CSV_PP.pm 
new/Text-CSV-2.03/lib/Text/CSV_PP.pm
--- old/Text-CSV-2.02/lib/Text/CSV_PP.pm        2022-08-07 22:20:44.000000000 
+0200
+++ new/Text-CSV-2.03/lib/Text/CSV_PP.pm        2023-08-11 09:08:38.000000000 
+0200
@@ -12,7 +12,7 @@
 use vars qw($VERSION @ISA @EXPORT_OK %EXPORT_TAGS);
 use Carp;
 
-$VERSION = '2.02';
+$VERSION = '2.03';
 @ISA = qw(Exporter);
 
 sub PV  { 0 }
@@ -51,7 +51,7 @@
         CSV_TYPE_NV
     )],
 );
-@EXPORT_OK = (qw(csv PV IV NV), @{$EXPORT_TAGS{CONSTANTS}});
+@EXPORT_OK = (qw(csv PV IV NV), @{$EXPORT_TAGS{'CONSTANTS'}});
 
 my $ERRORS = {
         # Generic errors
@@ -83,6 +83,7 @@
         2012 => "EOF - End of data in parsing input stream",
         2013 => "ESP - Specification error for fragments RFC7111",
         2014 => "ENF - Inconsistent number of fields",
+        2015 => "ERW - Empty row",
 
         # EIQ - Error Inside Quotes
         2021 => "EIQ - NL char inside quotes, binary off",
@@ -239,6 +240,7 @@
     _BOUND_COLUMNS             => undef,
     _AHEAD                     => undef,
     _FORMULA_CB     => undef,
+    _EMPTROW_CB     => undef,
 
     ENCODING                   => undef,
 );
@@ -384,8 +386,9 @@
     $last_new_error = Text::CSV_PP->SetDiag(0);
     defined $\ && !exists $attr{eol} and $self->{eol} = $\;
     bless $self, $class;
-    defined $self->{types} and $self->types ($self->{types});
-    defined $attr_formula  and $self->{formula} = _supported_formula($self, 
$attr_formula);
+    defined $self->{'types'}           and $self->types($self->{'types'});
+    defined $self->{'skip_empty_rows'} and $self->{'skip_empty_rows'} = 
_supported_skip_empty_rows($self, $self->{'skip_empty_rows'});
+    defined $attr_formula              and $self->{'formula'}         = 
_supported_formula($self, $attr_formula);
     $self;
 }
 
@@ -600,10 +603,33 @@
     $self->{strict};
     }
 
-sub skip_empty_rows {
+sub _supported_skip_empty_rows {
+    my ($self, $f) = @_;
+    defined $f or return 0;
+    if ($self && $f && ref $f && ref $f eq "CODE") {
+       $self->{'_EMPTROW_CB'} = $f;
+       return 6;
+       }
+    $f =~ m/^(?: 0 | undef         )$/xi ? 0 :
+    $f =~ m/^(?: 1 | skip          )$/xi ? 1 :
+    $f =~ m/^(?: 2 | eof   | stop  )$/xi ? 2 :
+    $f =~ m/^(?: 3 | die           )$/xi ? 3 :
+    $f =~ m/^(?: 4 | croak         )$/xi ? 4 :
+    $f =~ m/^(?: 5 | error         )$/xi ? 5 :
+    $f =~ m/^(?: 6 | cb            )$/xi ? 6 : do {
+       $self ||= "Text::CSV_PP";
+       croak ($self->_SetDiagInfo (1500, "skip_empty_rows '$f' is not 
supported"));
+       };
+    }
+
+ sub skip_empty_rows {
     my $self = shift;
-    @_ and $self->_set_attr_X ("skip_empty_rows", shift);
-    $self->{skip_empty_rows};
+    @_ and $self->_set_attr_N ("skip_empty_rows", _supported_skip_empty_rows 
($self, shift));
+    my $ser = $self->{'skip_empty_rows'};
+    $ser == 6 or $self->{'_EMPTROW_CB'} = undef;
+    $ser <= 1 ? $ser : $ser == 2 ? "eof"   : $ser == 3 ? "die"   :
+                      $ser == 4 ? "croak" : $ser == 5 ? "error" :
+                      $self->{'_EMPTROW_CB'};
     }
 
 sub _SetDiagInfo {
@@ -1115,7 +1141,7 @@
     ref $args{munge_column_names} eq "HASH" and
         @hdr = map { $args{munge_column_names}->{$_} || $_ } @hdr;
     my %hdr; $hdr{$_}++ for @hdr;
-    exists $hdr{""} and croak ($self->SetDiag (1012));
+    exists $hdr{''} and croak ($self->SetDiag (1012));
     unless (keys %hdr == @hdr) {
         croak ($self->_SetDiagInfo (1013, join ", " =>
             map { "$_ ($hdr{$_})" } grep { $hdr{$_} > 1 } keys %hdr));
@@ -1507,10 +1533,14 @@
     my @row1;
     if (defined $c->{hd_s} || defined $c->{hd_b} || defined $c->{hd_m} || 
defined $c->{hd_c}) {
         my %harg;
-        defined $c->{hd_s} and $harg{sep_set}            = $c->{hd_s};
-        defined $c->{hd_d} and $harg{detect_bom}         = $c->{hd_b};
-        defined $c->{hd_m} and $harg{munge_column_names} = $hdrs ? "none" : 
$c->{hd_m};
-        defined $c->{hd_c} and $harg{set_column_names}   = $hdrs ? 0      : 
$c->{hd_c};
+        !defined $c->{'hd_s'} &&  $c->{'attr'}{'sep_char'} and
+                 $c->{'hd_s'} = [ $c->{'attr'}{'sep_char'} ];
+        !defined $c->{'hd_s'} &&  $c->{'attr'}{'sep'} and
+                 $c->{'hd_s'} = [ $c->{'attr'}{'sep'} ];
+        defined  $c->{'hd_s'} and $harg{'sep_set'}            = $c->{'hd_s'};
+        defined  $c->{'hd_b'} and $harg{'detect_bom'}         = $c->{'hd_b'};
+        defined  $c->{'hd_m'} and $harg{'munge_column_names'} = $hdrs ? "none" 
: $c->{'hd_m'};
+        defined  $c->{'hd_c'} and $harg{'set_column_names'}   = $hdrs ? 0      
: $c->{'hd_c'};
         @row1 = $csv->header ($fh, \%harg);
         my @hdr = $csv->column_names;
         @hdr and $hdrs ||= \@hdr;
@@ -1535,28 +1565,33 @@
 
     $c->{fltr} && grep m/\D/ => keys %{$c->{fltr}} and $hdrs ||= "auto";
     if (defined $hdrs) {
-        if (!ref $hdrs) {
-            if ($hdrs eq "skip") {
-                $csv->getline ($fh); # discard;
+        if (!ref $hdrs or ref $hdrs eq "CODE") {
+            my $h = $c->{'hd_b'}
+                ? [ $csv->column_names () ]
+                :   $csv->getline ($fh);
+            my $has_h = $h && @$h;
+
+            if (ref $hdrs) {
+                $has_h or return;
+                my $cr = $hdrs;
+                $hdrs  = [ map {  $cr->($hdr{$_} || $_) } @{$h} ];
+                }
+            elsif ($hdrs eq "skip") {
+                # discard;
                 }
             elsif ($hdrs eq "auto") {
-                my $h = $csv->getline ($fh) or return;
+                $has_h or return;
                 $hdrs = [ map {      $hdr{$_} || $_ } @$h ];
                 }
             elsif ($hdrs eq "lc") {
-                my $h = $csv->getline ($fh) or return;
+                $has_h or return;
                 $hdrs = [ map { lc ($hdr{$_} || $_) } @$h ];
                 }
             elsif ($hdrs eq "uc") {
-                my $h = $csv->getline ($fh) or return;
+                $has_h or return;
                 $hdrs = [ map { uc ($hdr{$_} || $_) } @$h ];
                 }
             }
-        elsif (ref $hdrs eq "CODE") {
-            my $h  = $csv->getline ($fh) or return;
-            my $cr = $hdrs;
-            $hdrs  = [ map {  $cr->($hdr{$_} || $_) } @$h ];
-            }
         $c->{kh} and $hdrs and @{$c->{kh}} = @$hdrs;
         }
 
@@ -1588,7 +1623,7 @@
           do {
             my @h = $csv->column_names ($hdrs);
             my %h; $h{$_}++ for @h;
-            exists $h{""} and croak ($csv->SetDiag (1012));
+            exists $h{''} and croak ($csv->SetDiag (1012));
             unless (keys %h == @h) {
                 croak ($csv->_SetDiagInfo (1013, join ", " =>
                     map { "$_ ($h{$_})" } grep { $h{$_} > 1 } keys %h));
@@ -2539,12 +2574,46 @@
             elsif (defined $c and ($c eq "\012" or $c eq '' or (defined $eol 
and $c eq $eol and $eol ne "\015"))) { # EOL
     EOLX:
                 if ($fnum == 1 && $ctx->{flag} == 0 && (!$v_ref || $$v_ref eq 
'') && $ctx->{skip_empty_rows}) {
-                    $ctx->{fld_idx} = 0;
-                    $c = $self->__get($ctx, $src);
-                    if (!defined $c) {  # EOF
-                        $v_ref = undef;
-                        $waitingForField = 0;
-                        last LOOP;
+                    ### SkipEmptyRow
+                    my $ser = $ctx->{skip_empty_rows};
+                    if ($ser == 3) { $self->SetDiag(2015); die "Empty row\n"; }
+                    if ($ser == 4) { $self->SetDiag(2015); die "Empty row\n"; }
+                    if ($ser == 5) { $self->SetDiag(2015); return undef; }
+
+                    if ($ser <= 2) { # skip & eof
+                        $ctx->{fld_idx} = 0;
+                        $c = $self->__get($ctx, $src);
+                        if (!defined $c or $ser == 2) {  # EOF
+                            $v_ref = undef;
+                            $waitingForField = 0;
+                            if ($ser == 2) { return undef; }
+                            last LOOP;
+                        }
+                    }
+
+                    if ($ser == 6) {
+                        my $cb = $self->{_EMPTROW_CB};
+                        unless ($cb && ref $cb eq 'CODE') {
+                            return undef;  # A callback is wanted, but none 
found
+                        }
+                        local $_ = $v_ref;
+                        my $rv = $cb->();
+                        # Result should be a ref to a list.
+                        unless (ref $rv eq 'ARRAY') {
+                            return undef;
+                        }
+                        my $n = @$rv;
+                        if ($n <= 0) {
+                            return 1;
+                        }
+                        if ($ctx->{is_bound} && $ctx->{is_bound} < $n) {
+                            $n = $ctx->{is_bound} - 1;
+                        }
+                        for (my $i = 0; $i < $n; $i++) {
+                            my $rvi = $rv->[$i];
+                            $self->__push_value($ctx, \$rvi, $fields, $fflags, 
$ctx->{flag}, $fnum);
+                        }
+                        return 1;
                     }
                     goto RESTART;
                 }
@@ -2644,13 +2713,47 @@
                             $ctx->{used}--;
                             $ctx->{has_ahead} = 1;
                             if ($fnum == 1 && $ctx->{flag} == 0 && (!$v_ref or 
$$v_ref eq '') && $ctx->{skip_empty_rows}) {
-                                $ctx->{fld_idx} = 0;
-                                $c = $self->__get($ctx, $src);
-                                if (!defined $c) { # EOF
-                                    $v_ref = undef;
-                                    $waitingForField = 0;
-                                    last LOOP;
+                                ### SkipEmptyRow
+                                my $ser = $ctx->{skip_empty_rows};
+                                if ($ser == 3) { $self->SetDiag(2015); die 
"Empty row\n"; }
+                                if ($ser == 4) { $self->SetDiag(2015); die 
"Empty row\n"; }
+                                if ($ser == 5) { $self->SetDiag(2015); return 
undef; }
+
+                                if ($ser <= 2) { # skip & eof
+                                    $ctx->{fld_idx} = 0;
+                                    $c = $self->__get($ctx, $src);
+                                    if (!defined $c) { # EOF
+                                        $v_ref = undef;
+                                        $waitingForField = 0;
+                                        last LOOP;
+                                    }
+                                }
+
+                                if ($ser == 6) {
+                                    my $cb = $self->{_EMPTROW_CB};
+                                    unless ($cb && ref $cb eq 'CODE') {
+                                        return undef;  # A callback is wanted, 
but none found
+                                    }
+                                    local $_ = $v_ref;
+                                    my $rv = $cb->();
+                                    # Result should be a ref to a list.
+                                    unless (ref $rv eq 'ARRAY') {
+                                        return undef;
+                                    }
+                                    my $n = @$rv;
+                                    if ($n <= 0) {
+                                        return 1;
+                                    }
+                                    if ($ctx->{is_bound} && $ctx->{is_bound} < 
$n) {
+                                        $n = $ctx->{is_bound} - 1;
+                                    }
+                                    for (my $i = 0; $i < $n; $i++) {
+                                        my $rvi = $rv->[$i];
+                                        $self->__push_value($ctx, \$rvi, 
$fields, $fflags, $ctx->{flag}, $fnum);
+                                    }
+                                    return 1;
                                 }
+
                                 $$v_ref = $c2;
                                 goto RESTART;
                             }
@@ -2702,12 +2805,45 @@
                             $ctx->{used}--;
                             $ctx->{has_ahead} = 1;
                             if ($fnum == 1 && $ctx->{flag} == 0 && (!$v_ref or 
$$v_ref eq '') && $ctx->{skip_empty_rows}) {
-                                $ctx->{fld_idx} = 0;
-                                $c = $self->__get($ctx, $src);
-                                if (!defined $c) { # EOL
-                                    $v_ref = undef;
-                                    $waitingForField = 0;
-                                    last LOOP;
+                                ### SKipEmptyRow
+                                my $ser = $ctx->{skip_empty_rows};
+                                if ($ser == 3) { $self->SetDiag(2015); die 
"Empty row\n"; }
+                                if ($ser == 4) { $self->SetDiag(2015); die 
"Empty row\n"; }
+                                if ($ser == 5) { $self->SetDiag(2015); return 
undef; }
+
+                                if ($ser <= 2) { # skip & eof
+                                    $ctx->{fld_idx} = 0;
+                                    $c = $self->__get($ctx, $src);
+                                    if (!defined $c) { # EOL
+                                        $v_ref = undef;
+                                        $waitingForField = 0;
+                                        last LOOP;
+                                    }
+                                }
+
+                                if ($ser == 6) {
+                                    my $cb = $self->{_EMPTROW_CB};
+                                    unless ($cb && ref $cb eq 'CODE') {
+                                        return undef;  # A callback is wanted, 
but none found
+                                    }
+                                    local $_ = $v_ref;
+                                    my $rv = $cb->();
+                                    # Result should be a ref to a list.
+                                    unless (ref $rv eq 'ARRAY') {
+                                        return undef;
+                                    }
+                                    my $n = @$rv;
+                                    if ($n <= 0) {
+                                        return 1;
+                                    }
+                                    if ($ctx->{is_bound} && $ctx->{is_bound} < 
$n) {
+                                        $n = $ctx->{is_bound} - 1;
+                                    }
+                                    for (my $i = 0; $i < $n; $i++) {
+                                        my $rvi = $rv->[$i];
+                                        $self->__push_value($ctx, \$rvi, 
$fields, $fflags, $ctx->{flag}, $fnum);
+                                    }
+                                    return 1;
                                 }
                                 goto RESTART;
                             }
@@ -2807,6 +2943,8 @@
 
     if ($v_ref) {
         $self->__push_value($ctx, $v_ref, $fields, $fflags, $ctx->{flag}, 
$fnum);
+    } elsif ($ctx->{flag} == 0 && $fnum == 1 && $ctx->{skip_empty_rows} == 1) {
+        return undef;
     }
     return 1;
 }
@@ -2939,7 +3077,7 @@
 sub __push_value { # AV_PUSH (part of)
     my ($self, $ctx, $v_ref, $fields, $fflags, $flag, $fnum) = @_;
     utf8::encode($$v_ref) if $ctx->{utf8};
-    if ($ctx->{formula} && $$v_ref && substr($$v_ref, 0, 1) eq '=') {
+    if ($ctx->{formula} && defined $$v_ref && substr($$v_ref, 0, 1) eq '=') {
         my $value = $self->_formula($ctx, $$v_ref, $fnum);
         push @$fields, defined $value ? $value : undef;
         return;
@@ -3144,7 +3282,7 @@
                 headers => "auto");   # as array of hash
 
  # Write array of arrays as csv file
- csv (in => $aoa, out => "file.csv", sep_char=> ";");
+ csv (in => $aoa, out => "file.csv", sep_char => ";");
 
  # Only show lines where "code" is odd
  csv (in => "data.csv", filter => { code => sub { $_ % 2 }});
@@ -3428,14 +3566,82 @@
 =head3 skip_empty_rows
 
  my $csv = Text::CSV_PP->new ({ skip_empty_rows => 1 });
-         $csv->skip_empty_rows (0);
+         $csv->skip_empty_rows ("eof");
  my $f = $csv->skip_empty_rows;
 
-If this attribute is set to C<1>,  any row that has an  L</eol> immediately
-following the start of line will be skipped.  Default behavior is to return
-one single empty field.
+This attribute defines the behavior for empty rows:  an L</eol> immediately
+following the start of line. Default behavior is to return one single empty
+field.
+
+This attribute is only used in parsing.  This attribute is ineffective when
+using L</parse> and L</fields>.
+
+Possible values for this attribute are
+
+=over 2
 
-This attribute is only used in parsing.
+=item 0 | undef
+
+ my $csv = Text::CSV_PP->new ({ skip_empty_rows => 0 });
+ $csv->skip_empty_rows (undef);
+
+No special action is taken. The result will be one single empty field.
+
+=item 1 | "skip"
+
+ my $csv = Text::CSV_PP->new ({ skip_empty_rows => 1 });
+ $csv->skip_empty_rows ("skip");
+
+The row will be skipped.
+
+=item 2 | "eof" | "stop"
+
+ my $csv = Text::CSV_PP->new ({ skip_empty_rows => 2 });
+ $csv->skip_empty_rows ("eof");
+
+The parsing will stop as if an L</eof> was detected.
+
+=item 3 | "die"
+
+ my $csv = Text::CSV_PP->new ({ skip_empty_rows => 3 });
+ $csv->skip_empty_rows ("die");
+
+The parsing will stop.  The internal error code will be set to 2015 and the
+parser will C<die>.
+
+=item 4 | "croak"
+
+ my $csv = Text::CSV_PP->new ({ skip_empty_rows => 4 });
+ $csv->skip_empty_rows ("croak");
+
+The parsing will stop.  The internal error code will be set to 2015 and the
+parser will C<croak>.
+
+=item 5 | "error"
+
+ my $csv = Text::CSV_PP->new ({ skip_empty_rows => 5 });
+ $csv->skip_empty_rows ("error");
+
+The parsing will fail.  The internal error code will be set to 2015.
+
+=item callback
+
+ my $csv = Text::CSV_PP->new ({ skip_empty_rows => sub { [] } });
+ $csv->skip_empty_rows (sub { [ 42, $., undef, "empty" ] });
+
+The callback is invoked and its result used instead.  If you want the parse
+to stop after the callback, make sure to return a false value.
+
+The returned value from the callback should be an array-ref. Any other type
+will cause the parse to stop, so these are equivalent in behavior:
+
+ csv (in => $fh, skip_empty_rows => "stop");
+ csv (in => $fh. skip_empty_rows => sub { 0; });
+
+=back
+
+Without arguments, the current value is returned: C<0>, C<1>, C<eof>, C<die>,
+C<croak> or the callback.
 
 =head3 formula_handling
 
@@ -4053,7 +4259,8 @@
 This will return a reference to a list of L<getline ($fh)|/getline> results.
 In this call, C<keep_meta_info> is disabled.  If C<$offset> is negative, as
 with C<splice>, only the last  C<abs ($offset)> records of C<$fh> are taken
-into consideration.
+into consideration. Parameters C<$offset> and C<$length> are expected to be
+an integers. Non-integer values are interpreted as integer without check.
 
 Given a CSV file with 10 lines:
 
@@ -4942,6 +5149,8 @@
 
  my $aoa = csv (in => $fh, headers => "skip");
 
+C<skip> is invalid/ignored in combinations with L<C<detect_bom>|/detect_bom>.
+
 =item auto
 
 If C<auto> is used, the first line of the C<CSV> source will be read as the
@@ -5205,7 +5414,9 @@
 If C<sep_set> is set, the method L</header> is invoked on the opened stream
 to detect and set L<C<sep_char>|/sep_char> with the given set.
 
-C<sep_set> can be abbreviated to C<seps>.
+C<sep_set> can be abbreviated to C<seps>. If neither C<sep_set> not C<seps>
+is given, but C<sep> is defined, C<sep_set> defaults to C<[ sep ]>. This is
+only supported for perl version 5.10 and up.
 
 Note that as the  L</header> method is invoked,  its default is to also set
 the headers.
@@ -5787,6 +5998,11 @@
 Inconsistent number of fields under strict parsing.
 
 =item *
+2015 "ERW - Empty row"
+
+An empty row was not allowed.
+
+=item *
 2021 "EIQ - NL char inside quotes, binary off"
 
 Sequences like C<1,"foo\nbar",22,1> are allowed only when the binary option
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/t/12_acc.t new/Text-CSV-2.03/t/12_acc.t
--- old/Text-CSV-2.02/t/12_acc.t        2022-08-07 21:51:04.000000000 +0200
+++ new/Text-CSV-2.03/t/12_acc.t        2023-08-07 20:08:29.000000000 +0200
@@ -192,19 +192,26 @@
     foreach my $quo (undef, "", " ", "\t", "!!!!!!") {
        defined $esc && $esc =~ m/[ \t]/ or 
        defined $quo && $quo =~ m/[ \t]/ or next;
+       my $wc = join " " => map {
+               !defined $_ ? "<undef>" :
+               $_ eq ""    ? "<empty>" :
+               $_ eq " "   ? "<sp>"    :
+               $_ eq "\t"  ? "<tab>"   : $_ }
+           "esc:", $esc, "quo:", $quo;
        eval { $csv = Text::CSV->new ({
            escape           => $esc,
            quote            => $quo,
            allow_whitespace => 1,
            }) };
-       like ((Text::CSV::error_diag)[1], qr{^INI - allow_whitespace}, "Wrong 
combo - error message");
-       is   ((Text::CSV::error_diag)[0], 1002, "Wrong combo - numeric error");
+       like ((Text::CSV::error_diag)[1], qr{^INI - allow_whitespace}, "Wrong 
combo - error message: $wc");
+       is   ((Text::CSV::error_diag)[0], 1002, "Wrong combo - numeric error: 
$wc");
        }
     }
 
 # Test 1003 in constructor
 foreach my $x ("\r", "\n", "\r\n", "x\n", "\rx") {
     foreach my $attr (qw( sep_char quote_char escape_char )) {
+       #ok (1, "attr: $attr => ", $x =~ s/\n/\\n/gr =~ s/\r/\\r/gr);
        eval { $csv = Text::CSV->new ({ $attr => $x }) };
        is ((Text::CSV::error_diag)[0], 1003, "eol in $attr");
        }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/t/16_import.t 
new/Text-CSV-2.03/t/16_import.t
--- old/Text-CSV-2.02/t/16_import.t     1970-01-01 01:00:00.000000000 +0100
+++ new/Text-CSV-2.03/t/16_import.t     2023-08-07 20:08:29.000000000 +0200
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+
+use strict;
+$^W = 1;
+
+use Test::More tests => 41;
+
+BEGIN {
+    $ENV{PERL_TEXT_CSV} = $ENV{TEST_PERL_TEXT_CSV} || 0;
+    use_ok "Text::CSV", qw( :CONSTANTS PV IV NV );
+    plan skip_all => "Cannot load Text::CSV" if $@;
+    }
+
+is (              PV,             0, "Type PV");
+is (              IV,             1, "Type IV");
+is (              NV,             2, "Type NV");
+
+is (              PV (),          0, "Type PV f");
+is (              IV (),          1, "Type IV f");
+is (              NV (),          2, "Type NV f");
+
+is (Text::CSV::PV,             0, "Type T:C:PV");
+is (Text::CSV::IV,             1, "Type T:C:IV");
+is (Text::CSV::NV,             2, "Type T:C:NV");
+
+is (Text::CSV::PV (),          0, "Type T:C:PV f");
+is (Text::CSV::IV (),          1, "Type T:C:IV f");
+is (Text::CSV::NV (),          2, "Type T:C:NV f");
+
+is (              CSV_TYPE_PV,    0, "Type CT_PV");
+is (              CSV_TYPE_IV,    1, "Type CT_IV");
+is (              CSV_TYPE_NV,    2, "Type CT_NV");
+
+is (              CSV_TYPE_PV (), 0, "Type CT_PV f");
+is (              CSV_TYPE_IV (), 1, "Type CT_IV f");
+is (              CSV_TYPE_NV (), 2, "Type CT_NV f");
+
+is (Text::CSV::CSV_TYPE_PV,    0, "Type T:C:CT_PV");
+is (Text::CSV::CSV_TYPE_IV,    1, "Type T:C:CT_IV");
+is (Text::CSV::CSV_TYPE_NV,    2, "Type T:C:CT_NV");
+
+is (Text::CSV::CSV_TYPE_PV (), 0, "Type T:C:CT_PV f");
+is (Text::CSV::CSV_TYPE_IV (), 1, "Type T:C:CT_IV f");
+is (Text::CSV::CSV_TYPE_NV (), 2, "Type T:C:CT_NV f");
+
+is (              CSV_FLAGS_IS_QUOTED,          1, "is_Q");
+is (              CSV_FLAGS_IS_BINARY,          2, "is_B");
+is (              CSV_FLAGS_ERROR_IN_FIELD,     4, "is_E");
+is (              CSV_FLAGS_IS_MISSING,        16, "is_M");
+
+is (              CSV_FLAGS_IS_QUOTED (),       1, "is_Q f");
+is (              CSV_FLAGS_IS_BINARY (),       2, "is_B f");
+is (              CSV_FLAGS_ERROR_IN_FIELD (),  4, "is_E f");
+is (              CSV_FLAGS_IS_MISSING (),     16, "is_M f");
+
+is (Text::CSV::CSV_FLAGS_IS_QUOTED,          1, "is_Q");
+is (Text::CSV::CSV_FLAGS_IS_BINARY,          2, "is_B");
+is (Text::CSV::CSV_FLAGS_ERROR_IN_FIELD,     4, "is_E");
+is (Text::CSV::CSV_FLAGS_IS_MISSING,        16, "is_M");
+
+is (Text::CSV::CSV_FLAGS_IS_QUOTED (),       1, "T:C:is_Q f");
+is (Text::CSV::CSV_FLAGS_IS_BINARY (),       2, "T:C:is_B f");
+is (Text::CSV::CSV_FLAGS_ERROR_IN_FIELD (),  4, "T:C:is_E f");
+is (Text::CSV::CSV_FLAGS_IS_MISSING (),     16, "T:C:is_M f");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/t/67_emptrow.t 
new/Text-CSV-2.03/t/67_emptrow.t
--- old/Text-CSV-2.02/t/67_emptrow.t    1970-01-01 01:00:00.000000000 +0100
+++ new/Text-CSV-2.03/t/67_emptrow.t    2023-08-11 08:24:05.000000000 +0200
@@ -0,0 +1,126 @@
+#!/usr/bin/perl
+
+use strict;
+$^W = 1;
+
+use Test::More;
+
+BEGIN {
+    if ($] < 5.008001) {
+        plan skip_all => "This test unit requires perl-5.8.1 or higher";
+       }
+    else {
+       plan tests => 47;
+       }
+
+    $ENV{PERL_TEXT_CSV} = $ENV{TEST_PERL_TEXT_CSV} || 0;
+
+    use_ok "Text::CSV", ("csv");
+    plan skip_all => "Cannot load Text::CSV" if $@;
+    }
+my $tfn = "_67test.csv"; END { -f $tfn and unlink $tfn; }
+
+ok (my $csv = Text::CSV->new,                  "new");
+
+is ($csv->skip_empty_rows,             0,              "default");
+is ($csv->skip_empty_rows (1),         1,              "+1");
+is ($csv->skip_empty_rows ("skip"),    1,              "skip");
+is ($csv->skip_empty_rows ("SKIP"),    1,              "SKIP");
+is ($csv->skip_empty_rows (2),         "eof",          "+2");
+is ($csv->skip_empty_rows ("eof"),     "eof",          "eof");
+is ($csv->skip_empty_rows ("EOF"),     "eof",          "EOF");
+is ($csv->skip_empty_rows ("stop"),    "eof",          "stop");
+is ($csv->skip_empty_rows ("STOP"),    "eof",          "STOP");
+is ($csv->skip_empty_rows (3),         "die",          "+3");
+is ($csv->skip_empty_rows ("die"),     "die",          "die");
+is ($csv->skip_empty_rows ("DIE"),     "die",          "DIE");
+is ($csv->skip_empty_rows (4),         "croak",        "+4");
+is ($csv->skip_empty_rows ("croak"),   "croak",        "croak");
+is ($csv->skip_empty_rows ("CROAK"),   "croak",        "CROAK");
+is ($csv->skip_empty_rows (5),         "error",        "+5");
+is ($csv->skip_empty_rows ("error"),   "error",        "error");
+is ($csv->skip_empty_rows ("ERROR"),   "error",        "ERROR");
+
+sub cba { [      3,      42,      undef,      3 ] }
+sub cbh { { a => 3, b => 42, c => undef, d => 3 } }
+
+is ($csv->skip_empty_rows (\&cba),     \&cba,          "callback");
+
+is ($csv->skip_empty_rows (0),         0,              "+0");
+is ($csv->skip_empty_rows (undef),     0,              "undef");
+
+open my $fh, ">", $tfn;
+print $fh "a,b,c,d\n";
+print $fh "1,2,0,4\n";
+print $fh "4,0,9,1\n";
+print $fh "\n";
+print $fh "8,2,7,1\n";
+print $fh "\n";
+print $fh "\n";
+print $fh "5,7,9,3\n";
+print $fh "\n";
+close $fh;
+
+my @parg = (auto_diag => 0, in => $tfn);
+my @head = ([qw( a b c d )], [1,2,0,4], [4,0,9,1]);
+my @repl = (1..4);
+my $ea   = \@repl;
+
+# Array behavior
+is_deeply (csv (@parg, skip_empty_rows => 0), [ @head,
+    [""],[8,2,7,1],[""],[""],[5,7,9,3],[""]],                  "A Default");
+
+is_deeply (csv (@parg, skip_empty_rows => 1), [ @head,
+    [8,2,7,1],[5,7,9,3]],                                      "A Skip");
+
+is_deeply (csv (@parg, skip_empty_rows => 2),  \@head,         "A EOF");
+
+is (eval { csv (@parg, skip_empty_rows => 3); }, undef,                "A 
die");
+like ($@, qr{^Empty row},                                      "A msg");
+
+is (eval { csv (@parg, skip_empty_rows => 4); }, undef,                "A 
croak");
+like ($@, qr{^Empty row},                                      "A msg");
+
+$@ = "";
+$csv = Text::CSV->new ({ skip_empty_rows => 5 });
+is_deeply ($csv->csv (@parg), \@head,                          "A error");
+is ($@, "",                                                    "A msg");
+is (0 + $csv->error_diag, 2015,                                        "A 
code");
+
+is_deeply (csv (@parg, skip_empty_rows => sub {\@repl}), [ @head,
+    $ea,[8,2,7,1],$ea,$ea,[5,7,9,3],$ea],                      "A Callback");
+is_deeply (csv (@parg, skip_empty_rows => sub {0}), \@head,    "A Callback 0");
+
+# Hash behavior
+push @parg => bom => 1;
+my $eh = { a => "", b => undef, c => undef, d => undef },
+@head = ({ a => 1,  b => 2,     c => 0,     d => 4 },
+        { a => 4,  b => 0,     c => 9,     d => 1 });
+is_deeply (csv (@parg, skip_empty_rows => 0), [ @head, $eh,
+    { a => 8, b => 2, c => 7, d => 1 },$eh,$eh,
+    { a => 5, b => 7, c => 9, d => 3 },$eh],                   "H Default");
+
+is_deeply (csv (@parg, skip_empty_rows => 1), [ @head,
+    { a => 8, b => 2, c => 7, d => 1 },
+    { a => 5, b => 7, c => 9, d => 3 }],                       "H Skip");
+
+is_deeply (csv (@parg, skip_empty_rows => 2),  \@head,         "H EOF");
+
+is (eval { csv (@parg, skip_empty_rows => 3); }, undef,                "H 
die");
+like ($@, qr{^Empty row},                                      "H msg");
+
+is (eval { csv (@parg, skip_empty_rows => 4); }, undef,                "H 
croak");
+like ($@, qr{^Empty row},                                      "H msg");
+
+$@ = "";
+$csv = Text::CSV->new ({ skip_empty_rows => 5 });
+is_deeply ($csv->csv (@parg), \@head,                          "H error");
+is ($@, "",                                                    "H msg");
+is (0 + $csv->error_diag, 2015,                                        "H 
code");
+
+$eh = { a => 1, b => 2, c => 3, d => 4 };
+is_deeply (csv (@parg, skip_empty_rows => sub {\@repl}), [ @head, $eh,
+    { a => 8, b => 2, c => 7, d => 1 },$eh,$eh,
+    { a => 5, b => 7, c => 9, d => 3 },$eh],                   "H Callback");
+
+is_deeply (csv (@parg, skip_empty_rows => sub {0}), \@head,    "H Callback 0");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/t/68_header.t 
new/Text-CSV-2.03/t/68_header.t
--- old/Text-CSV-2.02/t/68_header.t     1970-01-01 01:00:00.000000000 +0100
+++ new/Text-CSV-2.03/t/68_header.t     2023-08-07 20:08:29.000000000 +0200
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+
+use strict;
+$^W = 1;
+
+use Test::More;
+
+BEGIN {
+    if ($] < 5.008001) {
+        plan skip_all => "This test unit requires perl-5.8.1 or higher";
+       }
+    else {
+       plan tests => 32;
+       }
+
+    $ENV{PERL_TEXT_CSV} = $ENV{TEST_PERL_TEXT_CSV} || 0;
+
+    use_ok "Text::CSV", "csv";
+    require "./t/util.pl";
+    }
+
+my $tfn = "_68test.csv"; END { unlink $tfn, "_$tfn"; }
+
+my @dta = (
+    [qw( foo  bar   zap                )],
+    [qw( mars venus pluto      )],
+    [qw( 1    2     3          )],
+    );
+my @dth = (
+    { foo => "mars", bar => "venus", zap => "pluto" },
+    { foo => 1,      bar => 2,       zap => 3       },
+    );
+
+{   open my $fh, ">", $tfn or die "$tfn: $!\n";
+    local $" = ",";
+    print $fh "@$_\n" for @dta;
+    close $fh;
+    }
+
+is_deeply (csv (in => $tfn),                              \@dta, "csv ()");
+is_deeply (csv (in => $tfn, bom => 1),                    \@dth, "csv (bom)");
+is_deeply (csv (in => $tfn,           headers => "auto"), \@dth, "csv 
(headers)");
+is_deeply (csv (in => $tfn, bom => 1, headers => "auto"), \@dth, "csv (bom, 
headers)");
+
+foreach my $arg ("", "bom", "auto", "bom, auto") {
+    open my $fh, "<", $tfn or die "$tfn: $!\n";
+    my %attr;
+    $arg =~ m/bom/     and $attr{bom}     = 1;
+    $arg =~ m/auto/    and $attr{headers} = "auto";
+    ok (my $csv = Text::CSV->new (), "New ($arg)");
+    is ($csv->record_number, 0, "start");
+    if ($arg) {
+       is_deeply ([ $csv->header   ($fh, \%attr) ], $dta[0], "Header") if $arg;
+       is ($csv->record_number, 1, "first data-record");
+       is_deeply ($csv->getline_hr ($fh), $dth[$_], "getline $_") for 0..$#dth;
+       }
+    else {
+       is_deeply ($csv->getline    ($fh), $dta[$_], "getline $_") for 0..$#dta;
+       }
+    is ($csv->record_number, 3, "done");
+    close $fh;
+    }
+
+done_testing;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/t/77_getall.t 
new/Text-CSV-2.03/t/77_getall.t
--- old/Text-CSV-2.02/t/77_getall.t     2022-08-07 21:51:04.000000000 +0200
+++ new/Text-CSV-2.03/t/77_getall.t     2023-08-07 20:08:29.000000000 +0200
@@ -3,7 +3,7 @@
 use strict;
 $^W = 1;
 
-use Test::More tests => 61;
+use Test::More tests => 81;
 
 BEGIN {
     $ENV{PERL_TEXT_CSV} = $ENV{TEST_PERL_TEXT_CSV} || 0;
@@ -29,13 +29,19 @@
     $sub->(\@list);
     $sub->(\@list,         0);
     $sub->([@list[2,3]],   2);
-    $sub->([],             0,  0);
-    $sub->(\@list,         0, 10);
-    $sub->([@list[0,1]],   0,  2);
-    $sub->([@list[1,2]],   1,  2);
+    $sub->([],             0,   0);
+    $sub->(\@list,         0,  10);
+    $sub->([@list[0,1]],   0,   2);
+    $sub->([@list[1,2]],   1,   2);
+    $sub->([@list[1,2]], 1e0,   2);
+    $sub->([@list[1,2]], "1",   2);
     $sub->([@list[1..3]], -3);
-    $sub->([@list[1,2]],  -3,  2);
-    $sub->([@list[1..3]], -3,  3);
+    $sub->([@list[1,2]],  -3,   2);
+    $sub->([@list[1..3]], -3,   3);
+
+    $sub->([$list[0]],     0,   1);
+    $sub->([$list[0]],     0, 1e0);
+    $sub->([$list[0]],     0, "1");
     } # do_tests
 
 foreach my $eol ("\n", "\r") {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/t/80_diag.t 
new/Text-CSV-2.03/t/80_diag.t
--- old/Text-CSV-2.02/t/80_diag.t       2022-08-07 21:51:04.000000000 +0200
+++ new/Text-CSV-2.03/t/80_diag.t       2023-08-07 20:08:29.000000000 +0200
@@ -3,7 +3,7 @@
 use strict;
 $^W = 1;
 
- use Test::More tests => 335;
+ use Test::More tests => 345;
 #use Test::More "no_plan";
 
 my %err;
@@ -62,7 +62,7 @@
 parse_err 2037,  1, 11, 1, qq{\0 };
 
 {   my @warn;
-    local $SIG{__WARN__} = sub { push @warn, @_ };
+    local $SIG{__WARN__} = sub { push @warn => @_ };
     $csv->error_diag ();
     ok (@warn == 1, "Got error message");
     like ($warn[0], qr{^# CSV_(?:PP|XS) ERROR: 2037 - EIF}, "error content");
@@ -75,26 +75,26 @@
 is (Text::CSV->new ({ ecs_char => ":" }), undef, "Unsupported option");
 
 {   my @warn;
-    local $SIG{__WARN__} = sub { push @warn, @_ };
+    local $SIG{__WARN__} = sub { push @warn => @_ };
     Text::CSV::error_diag ();
     ok (@warn == 1, "Error_diag in void context ::");
     like ($warn[0], qr{^# CSV_(?:PP|XS) ERROR: 1000 - INI}, "error content");
     }
 {   my @warn;
-    local $SIG{__WARN__} = sub { push @warn, @_ };
+    local $SIG{__WARN__} = sub { push @warn => @_ };
     Text::CSV->error_diag ();
     ok (@warn == 1, "Error_diag in void context ->");
     like ($warn[0], qr{^# CSV_(?:PP|XS) ERROR: 1000 - INI}, "error content");
     }
 
 {   my @warn;
-    local $SIG{__WARN__} = sub { push @warn, @_ };
+    local $SIG{__WARN__} = sub { push @warn => @_ };
     is (Text::CSV->new ({ auto_diag => 0, ecs_char => ":" }), undef,
        "Unsupported option");
     ok (@warn == 0, "Error_diag in from new ({ auto_diag => 0})");
     }
 {   my @warn;
-    local $SIG{__WARN__} = sub { push @warn, @_ };
+    local $SIG{__WARN__} = sub { push @warn => @_ };
     is (Text::CSV->new ({ auto_diag => 1, ecs_char => ":" }), undef,
        "Unsupported option");
     ok (@warn == 1, "Error_diag in from new ({ auto_diag => 1})");
@@ -117,7 +117,7 @@
 ok (1, "Test auto_diag");
 $csv = Text::CSV->new ({ auto_diag => 1 });
 {   my @warn;
-    local $SIG{__WARN__} = sub { push @warn, @_ };
+    local $SIG{__WARN__} = sub { push @warn => @_ };
     is ($csv->{_RECNO}, 0, "No records read yet");
     is ($csv->parse ('"","'), 0, "1 - bad parse");
     ok (@warn == 1, "1 - One error");
@@ -125,7 +125,7 @@
     is ($csv->{_RECNO}, 1, "One record read");
     }
 {   my @warn;
-    local $SIG{__WARN__} = sub { push @warn, @_ };
+    local $SIG{__WARN__} = sub { push @warn => @_ };
     is ($csv->diag_verbose (3), 3, "Set diag_verbose");
     is ($csv->parse ('"","'), 0, "1 - bad parse");
     ok (@warn == 1, "1 - One error");
@@ -142,7 +142,7 @@
     }
 
 {   my @warn;
-    local $SIG{__WARN__} = sub { push @warn, @_ };
+    local $SIG{__WARN__} = sub { push @warn => @_ };
 
     # Invalid error_input calls
     is (Text::CSV::error_input (undef), undef, "Bad error_input call");
@@ -300,7 +300,7 @@
     is_deeply ($aoh, [{ 1 => 1, 2 => 2, 3 => 3 }], "Column dropped");
     my @e;
     eval {
-       local $SIG{__WARN__} = sub { push @e, @_ };
+       local $SIG{__WARN__} = sub { push @e => @_ };
        $aoh = Text::CSV::csv (in => $tfn, headers => "auto", strict => 1);
        };
     is_deeply ($aoh, [],                "Fail under strict");
@@ -315,7 +315,7 @@
     is_deeply ($aoh, [{ 1 => 1, 2 => 2, 3 => 3, 4 => undef }], "Column added");
     @e = ();
     eval {
-       local $SIG{__WARN__} = sub { push @e, @_ };
+       local $SIG{__WARN__} = sub { push @e => @_ };
        $aoh = Text::CSV::csv (in => $tfn, headers => "auto", strict => 1);
        };
     is_deeply ($aoh, [],                "Fail under strict");
@@ -340,7 +340,7 @@
     }
 
 SKIP: {
-    $] < 5.008 and skip qq{$] does not support ScalarIO}, 14;
+    $] < 5.008 and skip qq{$] does not support ScalarIO}, 24;
     foreach my $key ({}, sub {}, []) {
        my $csv = Text::CSV->new;
        my $x = eval { $csv->csv (in => \"a,b", key => $key) };
@@ -363,6 +363,17 @@
        my @diag = $csv->error_diag;
        is ($diag[0], 1503, "Invalid value type");
        }
+
+    foreach my $ser ("die", 4) {
+       ok (my $csv = Text::CSV->new ({ skip_empty_rows => $ser }),
+                                               "New CSV for SER $ser");
+       is (eval { $csv->csv (in => \"\n") }, undef,
+                                               "Parse empty line for SER 
$ser");
+       like ($@, qr{^Empty row},               "Message");
+       my @diag = $csv->error_diag;
+       is   ($diag[0], 2015,                   "Empty row");
+       like ($diag[1], qr{^ERW - Empty row},   "Error description");
+       }
     }
 
 # Issue 19: auto_diag > 1 does not die if ->header () is used
@@ -379,8 +390,8 @@
        my $ok = eval {
            open  $fh,   "<", $tfn or die "$tfn: $!\n";
            my $csv = Text::CSV->new ({ auto_diag => 2 });
-           $h and push @row, [ $csv->header ($fh) ];
-           while (my $row = $csv->getline ($fh)) { push @row, $row }
+           $h and push @row => [ $csv->header ($fh) ];
+           while (my $row = $csv->getline ($fh)) { push @row => $row }
            close $fh;
            1;
            };
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/t/90_csv.t new/Text-CSV-2.03/t/90_csv.t
--- old/Text-CSV-2.02/t/90_csv.t        2022-08-07 22:18:09.000000000 +0200
+++ new/Text-CSV-2.03/t/90_csv.t        2023-08-07 20:08:29.000000000 +0200
@@ -72,8 +72,9 @@
        is_deeply (\@hdr, [qw( foo bar baz )], "Headers kept for $alias");
        }
     foreach my $alias (qw( internal true yes 1 )) {
+       my $buf = "";
        ok (my $ref = csv (in => $tfn, kh => $alias), "csv (kh => $alias)");
-       ok (csv (in => $ref, out => \my $buf, kh => $alias, quote_space => 0, 
eol => "\n"), "get it back");
+       ok (csv (in => $ref, out => \$buf, kh => $alias, quote_space => 0, eol 
=> "\n"), "get it back");
        is ($buf, $data, "Headers kept for $alias");
        }
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Text-CSV-2.02/t/91_csv_cb.t 
new/Text-CSV-2.03/t/91_csv_cb.t
--- old/Text-CSV-2.02/t/91_csv_cb.t     2022-08-07 21:51:04.000000000 +0200
+++ new/Text-CSV-2.03/t/91_csv_cb.t     2023-08-07 20:08:29.000000000 +0200
@@ -4,7 +4,7 @@
 $^W = 1;
 
 #use Test::More "no_plan";
- use Test::More tests => 58;
+ use Test::More tests => 79;
 
 BEGIN {
     $ENV{PERL_TEXT_CSV} = $ENV{TEST_PERL_TEXT_CSV} || 0;
@@ -69,18 +69,21 @@
     { foo => 2, bar => "a b", baz => "" },
     ], "AOH with filter on column name");
 
-is_deeply (csv (in => $tfn, headers => "lc"),
-           [ { foo => 1, bar => 2,     baz => 3 },
-             { foo => 2, bar => "a b", baz => "" }],
-           "AOH with lc headers");
-is_deeply (csv (in => $tfn, headers => "uc"),
-           [ { FOO => 1, BAR => 2,     BAZ => 3 },
-             { FOO => 2, BAR => "a b", BAZ => "" }],
-           "AOH with lc headers");
-is_deeply (csv (in => $tfn, headers => sub { lcfirst uc $_[0] }),
-           [ { fOO => 1, bAR => 2,     bAZ => 3 },
-             { fOO => 2, bAR => "a b", bAZ => "" }],
-           "AOH with mangled headers");
+SKIP: {
+    $] < 5.008001 and skip "No HOH/xx support in $]", 3;
+    is_deeply (csv (in => $tfn, headers => "lc"),
+               [ { foo => 1, bar => 2,     baz => 3 },
+                 { foo => 2, bar => "a b", baz => "" }],
+               "AOH with lc headers");
+    is_deeply (csv (in => $tfn, headers => "uc"),
+               [ { FOO => 1, BAR => 2,     BAZ => 3 },
+                 { FOO => 2, BAR => "a b", BAZ => "" }],
+               "AOH with lc headers");
+    is_deeply (csv (in => $tfn, headers => sub { lcfirst uc $_[0] }),
+               [ { fOO => 1, bAR => 2,     bAZ => 3 },
+                 { fOO => 2, bAR => "a b", bAZ => "" }],
+               "AOH with mangled headers");
+    }
 
 SKIP: {
     $] < 5.008001 and skip "No BOM support in $]", 1;
@@ -134,7 +137,9 @@
            });
     }
 # Check content ref in on_in AOH with aliases %_
-{   %_ = ( brt => 42 );
+SKIP: {
+    $] < 5.008001 and skip "No AOH/alias support in $]", 7; # 6 in on_in, 1 
is_deeply
+    %_ = ( brt => 42 );
     my $aoa = csv (
        in          => $tfn,
        headers     => "auto",
@@ -177,7 +182,9 @@
     }
 
 
-{   ok (my $hr = csv (in => $tfn, key => "foo", on_in => sub {
+SKIP: {
+    $] < 5.008001 and skip "Too complicated test for $]", 2;
+    ok (my $hr = csv (in => $tfn, key => "foo", on_in => sub {
                        $_[1]{quz} = "B"; $_{ziq} = 2; }),
        "Get into hashref with key and on_in");
     is_deeply ($hr->{8}, {qw( bar 13 baz 18 foo 8 quz B ziq 2 )},
@@ -199,21 +206,24 @@
 EOD
 close $fh;
 
-is_deeply (csv (in => $tfn, filter => "not_blank"),
-           [[3,3,3],[5,7,9],["",""],["",""],["",""," ",""],
-            ["","",""],["",""," ",""],[8,13,18]],
-           "filter => not_blank");
-is_deeply (csv (in => $tfn, filter => "not_empty"),
-           [[3,3,3],[5,7,9],["",""," ",""],["",""," ",""],[8,13,18]],
-           "filter => not_empty");
-is_deeply (csv (in => $tfn, filter => "filled"),
-           [[3,3,3],[5,7,9],[8,13,18]],
-           "filter => filled");
-
-is_deeply (csv (in => $tfn, filter => sub {
-               grep { defined && m/\S/ } @{$_[1]} }),
-           [[3,3,3],[5,7,9],[8,13,18]],
-           "filter => filled");
+SKIP: {
+    $] < 5.008001 and skip "Too complicated test for $]", 4;
+    is_deeply (csv (in => $tfn, filter => "not_blank"),
+               [[3,3,3],[5,7,9],["",""],["",""],["",""," ",""],
+                ["","",""],["",""," ",""],[8,13,18]],
+               "filter => not_blank");
+    is_deeply (csv (in => $tfn, filter => "not_empty"),
+               [[3,3,3],[5,7,9],["",""," ",""],["",""," ",""],[8,13,18]],
+               "filter => not_empty");
+    is_deeply (csv (in => $tfn, filter => "filled"),
+               [[3,3,3],[5,7,9],[8,13,18]],
+               "filter => filled");
+
+    is_deeply (csv (in => $tfn, filter => sub {
+                   grep { defined && m/\S/ } @{$_[1]} }),
+               [[3,3,3],[5,7,9],[8,13,18]],
+               "filter => filled");
+    }
 
 # Count rows in different ways
 open  $fh, ">", $tfn or die "$tfn: $!";
@@ -241,11 +251,51 @@
     my $aoa = csv (in => $tfn, filter => { 0 => sub { $n++; 0; }});
     is ($n, 4, "Count rows with filter hash");
     }
-{   my $n = 0;
+SKIP: {
+    $] < 5.008001 and skip "Too complicated test for $]", 1;
+    my $n = 0;
     my $aoa = csv (in => $tfn, filter => sub { $n++; 0; });
     is ($n, 4, "Count rows with filter sub");
     }
-{   my $n = 0;
+SKIP: {
+    $] < 5.008001 and skip "Too complicated test for $]", 1;
+    my $n = 0;
     csv (in => $tfn, on_in => sub { $n++; 0; }, out => \"skip");
     is ($n, 4, "Count rows with on_in and skipped out");
     }
+
+# sep_set, seps, sep on problematic header
+foreach my $sep (",", ";", "\t") {
+    my $ph = "Problematic header";
+
+    open  $fh, ">", $tfn or die "$tfn: $!";
+    print $fh qq{foo${sep}"bar: a, b"${sep}"c;d"${sep}"e"\n};
+    print $fh qq{1${sep}2${sep}3${sep}4\n};
+    close $fh;
+
+    my $exp = [{
+       "foo"       => 1,
+       "bar: a, b" => 2,
+       "c;d"       => 3,
+       "e"         => 4,
+       }];
+
+    ok (csv (in => $tfn, allow_loose_quotes => 1), "$ph, AoA");
+
+    if ($] < 5.010000) {
+       ok (1, "Unsupported header feature for $] - sep: $sep") for 1..6;
+       next;
+       }
+
+    my @err;
+    is (eval {
+       local $SIG{__WARN__} = sub { push @err => @_ };
+       csv (in => $tfn, bom => 1);
+       }, undef, "$ph: cannot decide on sep");
+    like ($err[0], qr{ERROR: 1011\b}, "$ph: error 1011");
+
+    is_deeply (csv (in => $tfn, bom => 1, sep_set  => [ $sep ]), $exp, "$ph: 
sep_set");
+    is_deeply (csv (in => $tfn, bom => 1, seps     => [ $sep ]), $exp, "$ph: 
seps");
+    is_deeply (csv (in => $tfn, bom => 1, sep_char =>   $sep  ), $exp, "$ph: 
sep_char");
+    is_deeply (csv (in => $tfn, bom => 1, sep      =>   $sep  ), $exp, "$ph: 
sep");
+    }

Reply via email to