| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Text::CSV_XS; | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | # Copyright (c) 2007-2023 H.Merijn Brand.  All rights reserved. | 
| 4 |  |  |  |  |  |  | # Copyright (c) 1998-2001 Jochen Wiedmann. All rights reserved. | 
| 5 |  |  |  |  |  |  | # Copyright (c) 1997 Alan Citterman.       All rights reserved. | 
| 6 |  |  |  |  |  |  | # | 
| 7 |  |  |  |  |  |  | # This program is free software; you can redistribute it and/or | 
| 8 |  |  |  |  |  |  | # modify it under the same terms as Perl itself. | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | # HISTORY | 
| 11 |  |  |  |  |  |  | # | 
| 12 |  |  |  |  |  |  | # 0.24 - | 
| 13 |  |  |  |  |  |  | #    H.Merijn Brand (h.m.brand@xs4all.nl) | 
| 14 |  |  |  |  |  |  | # 0.10 - 0.23 | 
| 15 |  |  |  |  |  |  | #    Jochen Wiedmann | 
| 16 |  |  |  |  |  |  | # Based on (the original) Text::CSV by: | 
| 17 |  |  |  |  |  |  | #    Alan Citterman | 
| 18 |  |  |  |  |  |  |  | 
| 19 |  |  |  |  |  |  | require 5.006001; | 
| 20 |  |  |  |  |  |  |  | 
| 21 | 30 |  |  | 30 |  | 2049496 | use strict; | 
|  | 30 |  |  |  |  | 352 |  | 
|  | 30 |  |  |  |  | 914 |  | 
| 22 | 30 |  |  | 30 |  | 161 | use warnings; | 
|  | 30 |  |  |  |  | 53 |  | 
|  | 30 |  |  |  |  | 1089 |  | 
| 23 |  |  |  |  |  |  |  | 
| 24 |  |  |  |  |  |  | require Exporter; | 
| 25 | 30 |  |  | 30 |  | 176 | use XSLoader; | 
|  | 30 |  |  |  |  | 58 |  | 
|  | 30 |  |  |  |  | 795 |  | 
| 26 | 30 |  |  | 30 |  | 166 | use Carp; | 
|  | 30 |  |  |  |  | 67 |  | 
|  | 30 |  |  |  |  | 2175 |  | 
| 27 |  |  |  |  |  |  |  | 
| 28 | 30 |  |  | 30 |  | 242 | use vars qw( $VERSION @ISA @EXPORT_OK %EXPORT_TAGS ); | 
|  | 30 |  |  |  |  | 85 |  | 
|  | 30 |  |  |  |  | 8949 |  | 
| 29 |  |  |  |  |  |  | $VERSION = "1.50"; | 
| 30 |  |  |  |  |  |  | @ISA     = qw( Exporter ); | 
| 31 |  |  |  |  |  |  | XSLoader::load ("Text::CSV_XS", $VERSION); | 
| 32 |  |  |  |  |  |  |  | 
| 33 | 4 |  |  | 4 | 1 | 9 | sub PV { 0 } sub CSV_TYPE_PV { PV } | 
|  | 12 |  |  | 12 | 1 | 137 |  | 
| 34 | 4 |  |  | 4 | 1 | 10 | sub IV { 1 } sub CSV_TYPE_IV { IV } | 
|  | 12 |  |  | 12 | 1 | 1346 |  | 
| 35 | 4 |  |  | 4 | 1 | 9 | sub NV { 2 } sub CSV_TYPE_NV { NV } | 
|  | 12 |  |  | 12 | 1 | 71 |  | 
| 36 |  |  |  |  |  |  |  | 
| 37 | 11 |  |  | 11 | 1 | 60 | sub CSV_FLAGS_IS_QUOTED		{ 0x0001 } | 
| 38 | 12 |  |  | 12 | 1 | 60 | sub CSV_FLAGS_IS_BINARY		{ 0x0002 } | 
| 39 | 4 |  |  | 4 | 1 | 17 | sub CSV_FLAGS_ERROR_IN_FIELD	{ 0x0004 } | 
| 40 | 20 |  |  | 20 | 1 | 86 | sub CSV_FLAGS_IS_MISSING	{ 0x0010 } | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | %EXPORT_TAGS = ( | 
| 43 |  |  |  |  |  |  | CONSTANTS	=> [qw( | 
| 44 |  |  |  |  |  |  | CSV_FLAGS_IS_QUOTED | 
| 45 |  |  |  |  |  |  | CSV_FLAGS_IS_BINARY | 
| 46 |  |  |  |  |  |  | CSV_FLAGS_ERROR_IN_FIELD | 
| 47 |  |  |  |  |  |  | CSV_FLAGS_IS_MISSING | 
| 48 |  |  |  |  |  |  |  | 
| 49 |  |  |  |  |  |  | CSV_TYPE_PV | 
| 50 |  |  |  |  |  |  | CSV_TYPE_IV | 
| 51 |  |  |  |  |  |  | CSV_TYPE_NV | 
| 52 |  |  |  |  |  |  | )], | 
| 53 |  |  |  |  |  |  | ); | 
| 54 |  |  |  |  |  |  | @EXPORT_OK = (qw( csv PV IV NV ), @{$EXPORT_TAGS{CONSTANTS}}); | 
| 55 |  |  |  |  |  |  |  | 
| 56 |  |  |  |  |  |  | if ($] < 5.008002) { | 
| 57 | 30 |  |  | 30 |  | 224 | no warnings "redefine"; | 
|  | 30 |  |  |  |  | 59 |  | 
|  | 30 |  |  |  |  | 348523 |  | 
| 58 |  |  |  |  |  |  | *utf8::decode = sub {}; | 
| 59 |  |  |  |  |  |  | } | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | # version | 
| 62 |  |  |  |  |  |  | # | 
| 63 |  |  |  |  |  |  | #   class/object method expecting no arguments and returning the version | 
| 64 |  |  |  |  |  |  | #   number of Text::CSV.  there are no side-effects. | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | sub version { | 
| 67 | 2 |  |  | 2 | 1 | 596 | return $VERSION; | 
| 68 |  |  |  |  |  |  | } # version | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | # new | 
| 71 |  |  |  |  |  |  | # | 
| 72 |  |  |  |  |  |  | #   class/object method expecting no arguments and returning a reference to | 
| 73 |  |  |  |  |  |  | #   a newly created Text::CSV object. | 
| 74 |  |  |  |  |  |  |  | 
| 75 |  |  |  |  |  |  | my %def_attr = ( | 
| 76 |  |  |  |  |  |  | 'eol'			=> '', | 
| 77 |  |  |  |  |  |  | 'sep_char'			=> ',', | 
| 78 |  |  |  |  |  |  | 'quote_char'		=> '"', | 
| 79 |  |  |  |  |  |  | 'escape_char'		=> '"', | 
| 80 |  |  |  |  |  |  | 'binary'			=> 0, | 
| 81 |  |  |  |  |  |  | 'decode_utf8'		=> 1, | 
| 82 |  |  |  |  |  |  | 'auto_diag'			=> 0, | 
| 83 |  |  |  |  |  |  | 'diag_verbose'		=> 0, | 
| 84 |  |  |  |  |  |  | 'strict'			=> 0, | 
| 85 |  |  |  |  |  |  | 'blank_is_undef'		=> 0, | 
| 86 |  |  |  |  |  |  | 'empty_is_undef'		=> 0, | 
| 87 |  |  |  |  |  |  | 'allow_whitespace'		=> 0, | 
| 88 |  |  |  |  |  |  | 'allow_loose_quotes'	=> 0, | 
| 89 |  |  |  |  |  |  | 'allow_loose_escapes'	=> 0, | 
| 90 |  |  |  |  |  |  | 'allow_unquoted_escape'	=> 0, | 
| 91 |  |  |  |  |  |  | 'always_quote'		=> 0, | 
| 92 |  |  |  |  |  |  | 'quote_empty'		=> 0, | 
| 93 |  |  |  |  |  |  | 'quote_space'		=> 1, | 
| 94 |  |  |  |  |  |  | 'quote_binary'		=> 1, | 
| 95 |  |  |  |  |  |  | 'escape_null'		=> 1, | 
| 96 |  |  |  |  |  |  | 'keep_meta_info'		=> 0, | 
| 97 |  |  |  |  |  |  | 'verbatim'			=> 0, | 
| 98 |  |  |  |  |  |  | 'formula'			=> 0, | 
| 99 |  |  |  |  |  |  | 'skip_empty_rows'		=> 0, | 
| 100 |  |  |  |  |  |  | 'undef_str'			=> undef, | 
| 101 |  |  |  |  |  |  | 'comment_str'		=> undef, | 
| 102 |  |  |  |  |  |  | 'types'			=> undef, | 
| 103 |  |  |  |  |  |  | 'callbacks'			=> undef, | 
| 104 |  |  |  |  |  |  |  | 
| 105 |  |  |  |  |  |  | '_EOF'			=> "", | 
| 106 |  |  |  |  |  |  | '_RECNO'			=> 0, | 
| 107 |  |  |  |  |  |  | '_STATUS'			=> undef, | 
| 108 |  |  |  |  |  |  | '_FIELDS'			=> undef, | 
| 109 |  |  |  |  |  |  | '_FFLAGS'			=> undef, | 
| 110 |  |  |  |  |  |  | '_STRING'			=> undef, | 
| 111 |  |  |  |  |  |  | '_ERROR_INPUT'		=> undef, | 
| 112 |  |  |  |  |  |  | '_COLUMN_NAMES'		=> undef, | 
| 113 |  |  |  |  |  |  | '_BOUND_COLUMNS'		=> undef, | 
| 114 |  |  |  |  |  |  | '_AHEAD'			=> undef, | 
| 115 |  |  |  |  |  |  | '_FORMULA_CB'		=> undef, | 
| 116 |  |  |  |  |  |  |  | 
| 117 |  |  |  |  |  |  | 'ENCODING'			=> undef, | 
| 118 |  |  |  |  |  |  | ); | 
| 119 |  |  |  |  |  |  | my %attr_alias = ( | 
| 120 |  |  |  |  |  |  | 'quote_always'		=> "always_quote", | 
| 121 |  |  |  |  |  |  | 'verbose_diag'		=> "diag_verbose", | 
| 122 |  |  |  |  |  |  | 'quote_null'		=> "escape_null", | 
| 123 |  |  |  |  |  |  | 'escape'			=> "escape_char", | 
| 124 |  |  |  |  |  |  | 'comment'			=> "comment_str", | 
| 125 |  |  |  |  |  |  | ); | 
| 126 |  |  |  |  |  |  | my $last_new_err = Text::CSV_XS->SetDiag (0); | 
| 127 |  |  |  |  |  |  | my $ebcdic       = ord ("A") == 0xC1;	# Faster than $Config{'ebcdic'} | 
| 128 |  |  |  |  |  |  | my @internal_kh; | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | # NOT a method: is also used before bless | 
| 131 |  |  |  |  |  |  | sub _unhealthy_whitespace { | 
| 132 | 15611 |  |  | 15611 |  | 25925 | my ($self, $aw) = @_; | 
| 133 | 15611 | 100 |  |  |  | 43418 | $aw or return 0; # no checks needed without allow_whitespace | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 3564 |  |  |  |  | 5242 | my $quo = $self->{'quote'}; | 
| 136 | 3564 | 100 | 100 |  |  | 8361 | defined $quo && length ($quo) or $quo = $self->{'quote_char'}; | 
| 137 | 3564 |  |  |  |  | 5492 | my $esc = $self->{'escape_char'}; | 
| 138 |  |  |  |  |  |  |  | 
| 139 | 3564 | 100 | 100 |  |  | 35654 | defined $quo && $quo =~ m/^[ \t]/ and return 1002; | 
| 140 | 3322 | 100 | 100 |  |  | 36168 | defined $esc && $esc =~ m/^[ \t]/ and return 1002; | 
| 141 |  |  |  |  |  |  |  | 
| 142 | 3032 |  |  |  |  | 7189 | return 0; | 
| 143 |  |  |  |  |  |  | } # _unhealty_whitespace | 
| 144 |  |  |  |  |  |  |  | 
| 145 |  |  |  |  |  |  | sub _check_sanity { | 
| 146 | 12305 |  |  | 12305 |  | 16563 | my $self = shift; | 
| 147 |  |  |  |  |  |  |  | 
| 148 | 12305 |  |  |  |  | 19125 | my $eol = $self->{'eol'}; | 
| 149 | 12305 |  |  |  |  | 17309 | my $sep = $self->{'sep'}; | 
| 150 | 12305 | 100 | 100 |  |  | 30380 | defined $sep && length ($sep) or $sep = $self->{'sep_char'}; | 
| 151 | 12305 |  |  |  |  | 17829 | my $quo = $self->{'quote'}; | 
| 152 | 12305 | 100 | 100 |  |  | 25814 | defined $quo && length ($quo) or $quo = $self->{'quote_char'}; | 
| 153 | 12305 |  |  |  |  | 16822 | my $esc = $self->{'escape_char'}; | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | #    use DP;::diag ("SEP: '", DPeek ($sep), | 
| 156 |  |  |  |  |  |  | #	        "', QUO: '", DPeek ($quo), | 
| 157 |  |  |  |  |  |  | #	        "', ESC: '", DPeek ($esc),"'"); | 
| 158 |  |  |  |  |  |  |  | 
| 159 |  |  |  |  |  |  | # sep_char should not be undefined | 
| 160 | 12305 | 100 |  |  |  | 22830 | $sep ne ""			or  return 1008; | 
| 161 | 12303 | 100 |  |  |  | 23372 | length ($sep) > 16		and return 1006; | 
| 162 | 12302 | 100 |  |  |  | 32413 | $sep =~ m/[\r\n]/		and return 1003; | 
| 163 |  |  |  |  |  |  |  | 
| 164 | 12296 | 100 |  |  |  | 20870 | if (defined $quo) { | 
| 165 | 12286 | 100 |  |  |  | 47589 | $quo eq $sep		and return 1001; | 
| 166 | 12058 | 100 |  |  |  | 19931 | length ($quo) > 16	and return 1007; | 
| 167 | 12057 | 100 |  |  |  | 22723 | $quo =~ m/[\r\n]/	and return 1003; | 
| 168 |  |  |  |  |  |  | } | 
| 169 | 12061 | 100 |  |  |  | 20361 | if (defined $esc) { | 
| 170 | 12045 | 100 |  |  |  | 38728 | $esc eq $sep		and return 1001; | 
| 171 | 11877 | 100 |  |  |  | 21409 | $esc =~ m/[\r\n]/	and return 1003; | 
| 172 |  |  |  |  |  |  | } | 
| 173 | 11887 | 100 |  |  |  | 19572 | if (defined $eol) { | 
| 174 | 11883 | 100 |  |  |  | 19768 | length ($eol) > 16	and return 1005; | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 11886 |  |  |  |  | 21445 | return _unhealthy_whitespace ($self, $self->{'allow_whitespace'}); | 
| 178 |  |  |  |  |  |  | } # _check_sanity | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | sub known_attributes { | 
| 181 | 3 |  |  | 3 | 1 | 628 | sort grep !m/^_/ => "sep", "quote", keys %def_attr; | 
| 182 |  |  |  |  |  |  | } # known_attributes | 
| 183 |  |  |  |  |  |  |  | 
| 184 |  |  |  |  |  |  | sub new { | 
| 185 | 907 |  |  | 907 | 1 | 64379662 | $last_new_err = Text::CSV_XS->SetDiag (1000, | 
| 186 |  |  |  |  |  |  | "usage: my \$csv = Text::CSV_XS->new ([{ option => value, ... }]);"); | 
| 187 |  |  |  |  |  |  |  | 
| 188 | 907 |  |  |  |  | 2036 | my $proto = shift; | 
| 189 | 907 | 100 | 100 |  |  | 4298 | my $class = ref $proto || $proto	or  return; | 
| 190 | 906 | 100 | 100 |  |  | 3902 | @_ > 0 &&   ref $_[0] ne "HASH"	and return; | 
| 191 | 898 |  | 100 |  |  | 2101 | my $attr  = shift || {}; | 
| 192 |  |  |  |  |  |  | my %attr  = map { | 
| 193 | 2074 | 100 |  |  |  | 7984 | my $k = m/^[a-zA-Z]\w+$/ ? lc $_ : $_; | 
| 194 | 2074 | 100 |  |  |  | 4435 | exists $attr_alias{$k} and $k = $attr_alias{$k}; | 
| 195 | 2074 |  |  |  |  | 5692 | ($k => $attr->{$_}); | 
| 196 | 898 |  |  |  |  | 1313 | } keys %{$attr}; | 
|  | 898 |  |  |  |  | 2783 |  | 
| 197 |  |  |  |  |  |  |  | 
| 198 | 898 |  |  |  |  | 1921 | my $sep_aliased = 0; | 
| 199 | 898 | 100 |  |  |  | 1889 | if (exists $attr{'sep'}) { | 
| 200 | 10 |  |  |  |  | 33 | $attr{'sep_char'} = delete $attr{'sep'}; | 
| 201 | 10 |  |  |  |  | 41 | $sep_aliased = 1; | 
| 202 |  |  |  |  |  |  | } | 
| 203 | 898 |  |  |  |  | 1291 | my $quote_aliased = 0; | 
| 204 | 898 | 100 |  |  |  | 1729 | if (exists $attr{'quote'}) { | 
| 205 | 25 |  |  |  |  | 62 | $attr{'quote_char'} = delete $attr{'quote'}; | 
| 206 | 25 |  |  |  |  | 39 | $quote_aliased = 1; | 
| 207 |  |  |  |  |  |  | } | 
| 208 |  |  |  |  |  |  | exists $attr{'formula_handling'} and | 
| 209 | 898 | 100 |  |  |  | 1713 | $attr{'formula'} = delete $attr{'formula_handling'}; | 
| 210 | 898 |  |  |  |  | 1356 | my $attr_formula = delete $attr{'formula'}; | 
| 211 |  |  |  |  |  |  |  | 
| 212 | 898 |  |  |  |  | 2152 | for (keys %attr) { | 
| 213 | 2037 | 100 | 100 |  |  | 7114 | if (m/^[a-z]/ && exists $def_attr{$_}) { | 
| 214 |  |  |  |  |  |  | # uncoverable condition false | 
| 215 | 2030 | 100 | 100 |  |  | 6800 | defined $attr{$_} && m/_char$/ and utf8::decode ($attr{$_}); | 
| 216 | 2030 |  |  |  |  | 3426 | next; | 
| 217 |  |  |  |  |  |  | } | 
| 218 |  |  |  |  |  |  | #	croak? | 
| 219 | 7 |  |  |  |  | 36 | $last_new_err = Text::CSV_XS->SetDiag (1000, "INI - Unknown attribute '$_'"); | 
| 220 | 7 | 100 |  |  |  | 36 | $attr{'auto_diag'} and error_diag (); | 
| 221 | 7 |  |  |  |  | 37 | return; | 
| 222 |  |  |  |  |  |  | } | 
| 223 | 891 | 100 |  |  |  | 2044 | if ($sep_aliased) { | 
| 224 | 10 |  |  |  |  | 60 | my @b = unpack "U0C*", $attr{'sep_char'}; | 
| 225 | 10 | 100 |  |  |  | 49 | if (@b > 1) { | 
| 226 | 6 |  |  |  |  | 17 | $attr{'sep'} = $attr{'sep_char'}; | 
| 227 | 6 |  |  |  |  | 14 | $attr{'sep_char'} = "\0"; | 
| 228 |  |  |  |  |  |  | } | 
| 229 |  |  |  |  |  |  | else { | 
| 230 | 4 |  |  |  |  | 11 | $attr{'sep'} = undef; | 
| 231 |  |  |  |  |  |  | } | 
| 232 |  |  |  |  |  |  | } | 
| 233 | 891 | 100 | 100 |  |  | 1986 | if ($quote_aliased and defined $attr{'quote_char'}) { | 
| 234 | 21 |  |  |  |  | 79 | my @b = unpack "U0C*", $attr{'quote_char'}; | 
| 235 | 21 | 100 |  |  |  | 52 | if (@b > 1) { | 
| 236 | 7 |  |  |  |  | 14 | $attr{'quote'} = $attr{'quote_char'}; | 
| 237 | 7 |  |  |  |  | 17 | $attr{'quote_char'} = "\0"; | 
| 238 |  |  |  |  |  |  | } | 
| 239 |  |  |  |  |  |  | else { | 
| 240 | 14 |  |  |  |  | 32 | $attr{'quote'} = undef; | 
| 241 |  |  |  |  |  |  | } | 
| 242 |  |  |  |  |  |  | } | 
| 243 |  |  |  |  |  |  |  | 
| 244 | 891 |  |  |  |  | 15020 | my $self = { %def_attr, %attr }; | 
| 245 | 891 | 100 |  |  |  | 3235 | if (my $ec = _check_sanity ($self)) { | 
| 246 | 35 |  |  |  |  | 131 | $last_new_err = Text::CSV_XS->SetDiag ($ec); | 
| 247 | 35 | 100 |  |  |  | 83 | $attr{'auto_diag'} and error_diag (); | 
| 248 | 35 |  |  |  |  | 226 | return; | 
| 249 |  |  |  |  |  |  | } | 
| 250 | 856 | 100 | 100 |  |  | 2497 | if (defined $self->{'callbacks'} && ref $self->{'callbacks'} ne "HASH") { | 
| 251 | 6 |  |  |  |  | 727 | carp ("The 'callbacks' attribute is set but is not a hash: ignored\n"); | 
| 252 | 6 |  |  |  |  | 203 | $self->{'callbacks'} = undef; | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  |  | 
| 255 | 856 |  |  |  |  | 3252 | $last_new_err = Text::CSV_XS->SetDiag (0); | 
| 256 | 856 | 100 | 100 |  |  | 2686 | defined $\ && !exists $attr{'eol'} and $self->{'eol'} = $\; | 
| 257 | 856 |  |  |  |  | 1479 | bless $self, $class; | 
| 258 | 856 | 100 |  |  |  | 1922 | defined $self->{'types'} and $self->types ($self->{'types'}); | 
| 259 | 856 | 100 |  |  |  | 1636 | defined $attr_formula  and $self->{'formula'} = _supported_formula ($self, $attr_formula); | 
| 260 | 855 |  |  |  |  | 4818 | $self; | 
| 261 |  |  |  |  |  |  | } # new | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | # Keep in sync with XS! | 
| 264 |  |  |  |  |  |  | my %_cache_id = ( # Only expose what is accessed from within PM | 
| 265 |  |  |  |  |  |  | 'quote_char'		=>  0, | 
| 266 |  |  |  |  |  |  | 'escape_char'		=>  1, | 
| 267 |  |  |  |  |  |  | 'sep_char'			=>  2, | 
| 268 |  |  |  |  |  |  | 'sep'			=> 39,	# 39 .. 55 | 
| 269 |  |  |  |  |  |  | 'binary'			=>  3, | 
| 270 |  |  |  |  |  |  | 'keep_meta_info'		=>  4, | 
| 271 |  |  |  |  |  |  | 'always_quote'		=>  5, | 
| 272 |  |  |  |  |  |  | 'allow_loose_quotes'	=>  6, | 
| 273 |  |  |  |  |  |  | 'allow_loose_escapes'	=>  7, | 
| 274 |  |  |  |  |  |  | 'allow_unquoted_escape'	=>  8, | 
| 275 |  |  |  |  |  |  | 'allow_whitespace'		=>  9, | 
| 276 |  |  |  |  |  |  | 'blank_is_undef'		=> 10, | 
| 277 |  |  |  |  |  |  | 'eol'			=> 11, | 
| 278 |  |  |  |  |  |  | 'quote'			=> 15, | 
| 279 |  |  |  |  |  |  | 'verbatim'			=> 22, | 
| 280 |  |  |  |  |  |  | 'empty_is_undef'		=> 23, | 
| 281 |  |  |  |  |  |  | 'auto_diag'			=> 24, | 
| 282 |  |  |  |  |  |  | 'diag_verbose'		=> 33, | 
| 283 |  |  |  |  |  |  | 'quote_space'		=> 25, | 
| 284 |  |  |  |  |  |  | 'quote_empty'		=> 37, | 
| 285 |  |  |  |  |  |  | 'quote_binary'		=> 32, | 
| 286 |  |  |  |  |  |  | 'escape_null'		=> 31, | 
| 287 |  |  |  |  |  |  | 'decode_utf8'		=> 35, | 
| 288 |  |  |  |  |  |  | '_has_ahead'		=> 30, | 
| 289 |  |  |  |  |  |  | '_has_hooks'		=> 36, | 
| 290 |  |  |  |  |  |  | '_is_bound'			=> 26,	# 26 .. 29 | 
| 291 |  |  |  |  |  |  | 'formula'			=> 38, | 
| 292 |  |  |  |  |  |  | 'strict'			=> 42, | 
| 293 |  |  |  |  |  |  | 'skip_empty_rows'		=> 43, | 
| 294 |  |  |  |  |  |  | 'undef_str'			=> 46, | 
| 295 |  |  |  |  |  |  | 'comment_str'		=> 54, | 
| 296 |  |  |  |  |  |  | 'types'			=> 62, | 
| 297 |  |  |  |  |  |  | ); | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | # A `character' | 
| 300 |  |  |  |  |  |  | sub _set_attr_C { | 
| 301 | 11095 |  |  | 11095 |  | 22443 | my ($self, $name, $val, $ec) = @_; | 
| 302 | 11095 | 100 |  |  |  | 31379 | defined $val and utf8::decode ($val); | 
| 303 | 11095 |  |  |  |  | 18268 | $self->{$name} = $val; | 
| 304 | 11095 | 100 |  |  |  | 17058 | $ec = _check_sanity ($self) and croak ($self->SetDiag ($ec)); | 
| 305 | 10185 |  |  |  |  | 35940 | $self->_cache_set ($_cache_id{$name}, $val); | 
| 306 |  |  |  |  |  |  | } # _set_attr_C | 
| 307 |  |  |  |  |  |  |  | 
| 308 |  |  |  |  |  |  | # A flag | 
| 309 |  |  |  |  |  |  | sub _set_attr_X { | 
| 310 | 5642 |  |  | 5642 |  | 10072 | my ($self, $name, $val) = @_; | 
| 311 | 5642 | 100 |  |  |  | 10685 | defined $val or $val = 0; | 
| 312 | 5642 |  |  |  |  | 8949 | $self->{$name} = $val; | 
| 313 | 5642 |  |  |  |  | 21348 | $self->_cache_set ($_cache_id{$name}, 0 + $val); | 
| 314 |  |  |  |  |  |  | } # _set_attr_X | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | # A number | 
| 317 |  |  |  |  |  |  | sub _set_attr_N { | 
| 318 | 38 |  |  | 38 |  | 97 | my ($self, $name, $val) = @_; | 
| 319 | 38 |  |  |  |  | 82 | $self->{$name} = $val; | 
| 320 | 38 |  |  |  |  | 193 | $self->_cache_set ($_cache_id{$name}, 0 + $val); | 
| 321 |  |  |  |  |  |  | } # _set_attr_N | 
| 322 |  |  |  |  |  |  |  | 
| 323 |  |  |  |  |  |  | # Accessor methods. | 
| 324 |  |  |  |  |  |  | #   It is unwise to change them halfway through a single file! | 
| 325 |  |  |  |  |  |  | sub quote_char { | 
| 326 | 4836 |  |  | 4836 | 1 | 644060 | my $self = shift; | 
| 327 | 4836 | 100 |  |  |  | 10829 | if (@_) { | 
| 328 | 3601 |  |  |  |  | 8053 | $self->_set_attr_C ("quote_char", shift); | 
| 329 | 3374 |  |  |  |  | 7767 | $self->_cache_set ($_cache_id{'quote'}, ""); | 
| 330 |  |  |  |  |  |  | } | 
| 331 | 4609 |  |  |  |  | 13978 | $self->{'quote_char'}; | 
| 332 |  |  |  |  |  |  | } # quote_char | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | sub quote { | 
| 335 | 20 |  |  | 20 | 1 | 46 | my $self = shift; | 
| 336 | 20 | 100 |  |  |  | 60 | if (@_) { | 
| 337 | 11 |  |  |  |  | 23 | my $quote = shift; | 
| 338 | 11 | 100 |  |  |  | 59 | defined $quote or $quote = ""; | 
| 339 | 11 |  |  |  |  | 32 | utf8::decode ($quote); | 
| 340 | 11 |  |  |  |  | 45 | my @b = unpack "U0C*", $quote; | 
| 341 | 11 | 100 |  |  |  | 36 | if (@b > 1) { | 
| 342 | 5 | 100 |  |  |  | 101 | @b > 16 and croak ($self->SetDiag (1007)); | 
| 343 | 4 |  |  |  |  | 13 | $self->quote_char ("\0"); | 
| 344 |  |  |  |  |  |  | } | 
| 345 |  |  |  |  |  |  | else { | 
| 346 | 6 |  |  |  |  | 17 | $self->quote_char ($quote); | 
| 347 | 6 |  |  |  |  | 16 | $quote = ""; | 
| 348 |  |  |  |  |  |  | } | 
| 349 | 10 |  |  |  |  | 19 | $self->{'quote'} = $quote; | 
| 350 |  |  |  |  |  |  |  | 
| 351 | 10 |  |  |  |  | 19 | my $ec = _check_sanity ($self); | 
| 352 | 10 | 100 |  |  |  | 123 | $ec and croak ($self->SetDiag ($ec)); | 
| 353 |  |  |  |  |  |  |  | 
| 354 | 9 |  |  |  |  | 33 | $self->_cache_set ($_cache_id{'quote'}, $quote); | 
| 355 |  |  |  |  |  |  | } | 
| 356 | 18 |  |  |  |  | 30 | my $quote = $self->{'quote'}; | 
| 357 | 18 | 100 | 100 |  |  | 136 | defined $quote && length ($quote) ? $quote : $self->{'quote_char'}; | 
| 358 |  |  |  |  |  |  | } # quote | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | sub escape_char { | 
| 361 | 4826 |  |  | 4826 | 1 | 652237 | my $self = shift; | 
| 362 | 4826 | 100 |  |  |  | 11190 | if (@_) { | 
| 363 | 3595 |  |  |  |  | 5321 | my $ec = shift; | 
| 364 | 3595 |  |  |  |  | 8297 | $self->_set_attr_C ("escape_char", $ec); | 
| 365 | 3480 | 100 |  |  |  | 6887 | $ec or $self->_set_attr_X ("escape_null", 0); | 
| 366 |  |  |  |  |  |  | } | 
| 367 | 4711 |  |  |  |  | 14632 | $self->{'escape_char'}; | 
| 368 |  |  |  |  |  |  | } # escape_char | 
| 369 |  |  |  |  |  |  |  | 
| 370 |  |  |  |  |  |  | sub sep_char { | 
| 371 | 5142 |  |  | 5142 | 1 | 644133 | my $self = shift; | 
| 372 | 5142 | 100 |  |  |  | 12512 | if (@_) { | 
| 373 | 3899 |  |  |  |  | 8580 | $self->_set_attr_C ("sep_char", shift); | 
| 374 | 3331 |  |  |  |  | 7516 | $self->_cache_set ($_cache_id{'sep'}, ""); | 
| 375 |  |  |  |  |  |  | } | 
| 376 | 4574 |  |  |  |  | 13976 | $self->{'sep_char'}; | 
| 377 |  |  |  |  |  |  | } # sep_char | 
| 378 |  |  |  |  |  |  |  | 
| 379 |  |  |  |  |  |  | sub sep { | 
| 380 | 346 |  |  | 346 | 1 | 3123 | my $self = shift; | 
| 381 | 346 | 100 |  |  |  | 735 | if (@_) { | 
| 382 | 313 |  |  |  |  | 539 | my $sep = shift; | 
| 383 | 313 | 100 |  |  |  | 606 | defined $sep or $sep = ""; | 
| 384 | 313 |  |  |  |  | 943 | utf8::decode ($sep); | 
| 385 | 313 |  |  |  |  | 1099 | my @b = unpack "U0C*", $sep; | 
| 386 | 313 | 100 |  |  |  | 733 | if (@b > 1) { | 
| 387 | 13 | 100 |  |  |  | 121 | @b > 16 and croak ($self->SetDiag (1006)); | 
| 388 | 12 |  |  |  |  | 33 | $self->sep_char ("\0"); | 
| 389 |  |  |  |  |  |  | } | 
| 390 |  |  |  |  |  |  | else { | 
| 391 | 300 |  |  |  |  | 796 | $self->sep_char ($sep); | 
| 392 | 297 |  |  |  |  | 424 | $sep = ""; | 
| 393 |  |  |  |  |  |  | } | 
| 394 | 309 |  |  |  |  | 651 | $self->{'sep'} = $sep; | 
| 395 |  |  |  |  |  |  |  | 
| 396 | 309 |  |  |  |  | 561 | my $ec = _check_sanity ($self); | 
| 397 | 309 | 100 |  |  |  | 643 | $ec and croak ($self->SetDiag ($ec)); | 
| 398 |  |  |  |  |  |  |  | 
| 399 | 308 |  |  |  |  | 848 | $self->_cache_set ($_cache_id{'sep'}, $sep); | 
| 400 |  |  |  |  |  |  | } | 
| 401 | 341 |  |  |  |  | 626 | my $sep = $self->{'sep'}; | 
| 402 | 341 | 100 | 100 |  |  | 1376 | defined $sep && length ($sep) ? $sep : $self->{'sep_char'}; | 
| 403 |  |  |  |  |  |  | } # sep | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | sub eol { | 
| 406 | 157 |  |  | 157 | 1 | 6895 | my $self = shift; | 
| 407 | 157 | 100 |  |  |  | 383 | if (@_) { | 
| 408 | 125 |  |  |  |  | 229 | my $eol = shift; | 
| 409 | 125 | 100 |  |  |  | 285 | defined $eol or $eol = ""; | 
| 410 | 125 | 100 |  |  |  | 425 | length ($eol) > 16 and croak ($self->SetDiag (1005)); | 
| 411 | 124 |  |  |  |  | 230 | $self->{'eol'} = $eol; | 
| 412 | 124 |  |  |  |  | 445 | $self->_cache_set ($_cache_id{'eol'}, $eol); | 
| 413 |  |  |  |  |  |  | } | 
| 414 | 156 |  |  |  |  | 328 | $self->{'eol'}; | 
| 415 |  |  |  |  |  |  | } # eol | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | sub always_quote { | 
| 418 | 3032 |  |  | 3032 | 1 | 662820 | my $self = shift; | 
| 419 | 3032 | 100 |  |  |  | 8214 | @_ and $self->_set_attr_X ("always_quote", shift); | 
| 420 | 3032 |  |  |  |  | 8366 | $self->{'always_quote'}; | 
| 421 |  |  |  |  |  |  | } # always_quote | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | sub quote_space { | 
| 424 | 10 |  |  | 10 | 1 | 26 | my $self = shift; | 
| 425 | 10 | 100 |  |  |  | 52 | @_ and $self->_set_attr_X ("quote_space", shift); | 
| 426 | 10 |  |  |  |  | 44 | $self->{'quote_space'}; | 
| 427 |  |  |  |  |  |  | } # quote_space | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | sub quote_empty { | 
| 430 | 5 |  |  | 5 | 1 | 12 | my $self = shift; | 
| 431 | 5 | 100 |  |  |  | 22 | @_ and $self->_set_attr_X ("quote_empty", shift); | 
| 432 | 5 |  |  |  |  | 23 | $self->{'quote_empty'}; | 
| 433 |  |  |  |  |  |  | } # quote_empty | 
| 434 |  |  |  |  |  |  |  | 
| 435 |  |  |  |  |  |  | sub escape_null { | 
| 436 | 6 |  |  | 6 | 1 | 13 | my $self = shift; | 
| 437 | 6 | 100 |  |  |  | 23 | @_ and $self->_set_attr_X ("escape_null", shift); | 
| 438 | 6 |  |  |  |  | 30 | $self->{'escape_null'}; | 
| 439 |  |  |  |  |  |  | } # escape_null | 
| 440 | 3 |  |  | 3 | 1 | 12 | sub quote_null { goto &escape_null; } | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | sub quote_binary { | 
| 443 | 7 |  |  | 7 | 1 | 18 | my $self = shift; | 
| 444 | 7 | 100 |  |  |  | 42 | @_ and $self->_set_attr_X ("quote_binary", shift); | 
| 445 | 7 |  |  |  |  | 24 | $self->{'quote_binary'}; | 
| 446 |  |  |  |  |  |  | } # quote_binary | 
| 447 |  |  |  |  |  |  |  | 
| 448 |  |  |  |  |  |  | sub binary { | 
| 449 | 21 |  |  | 21 | 1 | 110781 | my $self = shift; | 
| 450 | 21 | 100 |  |  |  | 100 | @_ and $self->_set_attr_X ("binary", shift); | 
| 451 | 21 |  |  |  |  | 60 | $self->{'binary'}; | 
| 452 |  |  |  |  |  |  | } # binary | 
| 453 |  |  |  |  |  |  |  | 
| 454 |  |  |  |  |  |  | sub strict { | 
| 455 | 2 |  |  | 2 | 1 | 6 | my $self = shift; | 
| 456 | 2 | 100 |  |  |  | 8 | @_ and $self->_set_attr_X ("strict", shift); | 
| 457 | 2 |  |  |  |  | 8 | $self->{'strict'}; | 
| 458 |  |  |  |  |  |  | } # always_quote | 
| 459 |  |  |  |  |  |  |  | 
| 460 |  |  |  |  |  |  | sub skip_empty_rows { | 
| 461 | 2 |  |  | 2 | 1 | 4 | my $self = shift; | 
| 462 | 2 | 100 |  |  |  | 9 | @_ and $self->_set_attr_X ("skip_empty_rows", shift); | 
| 463 | 2 |  |  |  |  | 8 | $self->{'skip_empty_rows'}; | 
| 464 |  |  |  |  |  |  | } # always_quote | 
| 465 |  |  |  |  |  |  |  | 
| 466 |  |  |  |  |  |  | sub _SetDiagInfo { | 
| 467 | 17 |  |  | 17 |  | 40 | my ($self, $err, $msg) = @_; | 
| 468 | 17 |  |  |  |  | 138 | $self->SetDiag ($err); | 
| 469 | 17 |  |  |  |  | 42 | my $em  = $self->error_diag (); | 
| 470 | 17 | 50 |  |  |  | 71 | $em =~ s/^\d+$// and $msg =~ s/^/# /; | 
| 471 | 17 | 50 |  |  |  | 61 | my $sep = $em =~ m/[;\n]$/ ? "\n\t" : ": "; | 
| 472 | 17 |  |  |  |  | 1755 | join $sep => grep m/\S\S\S/ => $em, $msg; | 
| 473 |  |  |  |  |  |  | } # _SetDiagInfo | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | sub _supported_formula { | 
| 476 | 103 |  |  | 103 |  | 184 | my ($self, $f) = @_; | 
| 477 | 103 | 100 |  |  |  | 186 | defined $f or return 5; | 
| 478 | 102 | 100 | 66 |  |  | 480 | if ($self && $f && ref $f && ref $f eq "CODE") { | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 479 | 6 |  |  |  |  | 10 | $self->{'_FORMULA_CB'} = $f; | 
| 480 | 6 |  |  |  |  | 15 | return 6; | 
| 481 |  |  |  |  |  |  | } | 
| 482 |  |  |  |  |  |  | $f =~ m/^(?: 0 | none    )$/xi ? 0 : | 
| 483 |  |  |  |  |  |  | $f =~ m/^(?: 1 | die     )$/xi ? 1 : | 
| 484 |  |  |  |  |  |  | $f =~ m/^(?: 2 | croak   )$/xi ? 2 : | 
| 485 |  |  |  |  |  |  | $f =~ m/^(?: 3 | diag    )$/xi ? 3 : | 
| 486 |  |  |  |  |  |  | $f =~ m/^(?: 4 | empty | )$/xi ? 4 : | 
| 487 |  |  |  |  |  |  | $f =~ m/^(?: 5 | undef   )$/xi ? 5 : | 
| 488 | 96 | 100 |  |  |  | 840 | $f =~ m/^(?: 6 | cb      )$/xi ? 6 : do { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 489 | 7 |  | 50 |  |  | 16 | $self ||= "Text::CSV_XS"; | 
| 490 | 7 |  |  |  |  | 30 | croak ($self->_SetDiagInfo (1500, "formula-handling '$f' is not supported")); | 
| 491 |  |  |  |  |  |  | }; | 
| 492 |  |  |  |  |  |  | } # _supported_formula | 
| 493 |  |  |  |  |  |  |  | 
| 494 |  |  |  |  |  |  | sub formula { | 
| 495 | 44 |  |  | 44 | 1 | 3152 | my $self = shift; | 
| 496 | 44 | 100 |  |  |  | 136 | @_ and $self->_set_attr_N ("formula", _supported_formula ($self, shift)); | 
| 497 | 38 | 100 |  |  |  | 115 | $self->{'formula'} == 6 or $self->{'_FORMULA_CB'} = undef; | 
| 498 | 38 |  |  |  |  | 126 | [qw( none die croak diag empty undef cb )]->[_supported_formula ($self, $self->{'formula'})]; | 
| 499 |  |  |  |  |  |  | } # always_quote | 
| 500 |  |  |  |  |  |  | sub formula_handling { | 
| 501 | 7 |  |  | 7 | 1 | 14 | my $self = shift; | 
| 502 | 7 |  |  |  |  | 18 | $self->formula (@_); | 
| 503 |  |  |  |  |  |  | } # formula_handling | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | sub decode_utf8 { | 
| 506 | 2 |  |  | 2 | 1 | 6 | my $self = shift; | 
| 507 | 2 | 100 |  |  |  | 9 | @_ and $self->_set_attr_X ("decode_utf8", shift); | 
| 508 | 2 |  |  |  |  | 9 | $self->{'decode_utf8'}; | 
| 509 |  |  |  |  |  |  | } # decode_utf8 | 
| 510 |  |  |  |  |  |  |  | 
| 511 |  |  |  |  |  |  | sub keep_meta_info { | 
| 512 | 12 |  |  | 12 | 1 | 917 | my $self = shift; | 
| 513 | 12 | 100 |  |  |  | 45 | if (@_) { | 
| 514 | 11 |  |  |  |  | 19 | my $v = shift; | 
| 515 | 11 | 100 | 100 |  |  | 67 | !defined $v || $v eq "" and $v = 0; | 
| 516 | 11 | 100 |  |  |  | 61 | $v =~ m/^[0-9]/ or $v = lc $v eq "false" ? 0 : 1; # true/truth = 1 | 
|  |  | 100 |  |  |  |  |  | 
| 517 | 11 |  |  |  |  | 33 | $self->_set_attr_X ("keep_meta_info", $v); | 
| 518 |  |  |  |  |  |  | } | 
| 519 | 12 |  |  |  |  | 59 | $self->{'keep_meta_info'}; | 
| 520 |  |  |  |  |  |  | } # keep_meta_info | 
| 521 |  |  |  |  |  |  |  | 
| 522 |  |  |  |  |  |  | sub allow_loose_quotes { | 
| 523 | 12 |  |  | 12 | 1 | 27 | my $self = shift; | 
| 524 | 12 | 100 |  |  |  | 48 | @_ and $self->_set_attr_X ("allow_loose_quotes", shift); | 
| 525 | 12 |  |  |  |  | 29 | $self->{'allow_loose_quotes'}; | 
| 526 |  |  |  |  |  |  | } # allow_loose_quotes | 
| 527 |  |  |  |  |  |  |  | 
| 528 |  |  |  |  |  |  | sub allow_loose_escapes { | 
| 529 | 12 |  |  | 12 | 1 | 1107 | my $self = shift; | 
| 530 | 12 | 100 |  |  |  | 86 | @_ and $self->_set_attr_X ("allow_loose_escapes", shift); | 
| 531 | 12 |  |  |  |  | 45 | $self->{'allow_loose_escapes'}; | 
| 532 |  |  |  |  |  |  | } # allow_loose_escapes | 
| 533 |  |  |  |  |  |  |  | 
| 534 |  |  |  |  |  |  | sub allow_whitespace { | 
| 535 | 4954 |  |  | 4954 | 1 | 2218847 | my $self = shift; | 
| 536 | 4954 | 100 |  |  |  | 13299 | if (@_) { | 
| 537 | 3725 |  |  |  |  | 4975 | my $aw = shift; | 
| 538 | 3725 | 100 |  |  |  | 7364 | _unhealthy_whitespace ($self, $aw) and | 
| 539 |  |  |  |  |  |  | croak ($self->SetDiag (1002)); | 
| 540 | 3721 |  |  |  |  | 8658 | $self->_set_attr_X ("allow_whitespace", $aw); | 
| 541 |  |  |  |  |  |  | } | 
| 542 | 4950 |  |  |  |  | 15404 | $self->{'allow_whitespace'}; | 
| 543 |  |  |  |  |  |  | } # allow_whitespace | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | sub allow_unquoted_escape { | 
| 546 | 3 |  |  | 3 | 1 | 9 | my $self = shift; | 
| 547 | 3 | 100 |  |  |  | 15 | @_ and $self->_set_attr_X ("allow_unquoted_escape", shift); | 
| 548 | 3 |  |  |  |  | 19 | $self->{'allow_unquoted_escape'}; | 
| 549 |  |  |  |  |  |  | } # allow_unquoted_escape | 
| 550 |  |  |  |  |  |  |  | 
| 551 |  |  |  |  |  |  | sub blank_is_undef { | 
| 552 | 2 |  |  | 2 | 1 | 6 | my $self = shift; | 
| 553 | 2 | 100 |  |  |  | 9 | @_ and $self->_set_attr_X ("blank_is_undef", shift); | 
| 554 | 2 |  |  |  |  | 17 | $self->{'blank_is_undef'}; | 
| 555 |  |  |  |  |  |  | } # blank_is_undef | 
| 556 |  |  |  |  |  |  |  | 
| 557 |  |  |  |  |  |  | sub empty_is_undef { | 
| 558 | 2 |  |  | 2 | 1 | 5 | my $self = shift; | 
| 559 | 2 | 100 |  |  |  | 12 | @_ and $self->_set_attr_X ("empty_is_undef", shift); | 
| 560 | 2 |  |  |  |  | 12 | $self->{'empty_is_undef'}; | 
| 561 |  |  |  |  |  |  | } # empty_is_undef | 
| 562 |  |  |  |  |  |  |  | 
| 563 |  |  |  |  |  |  | sub verbatim { | 
| 564 | 9 |  |  | 9 | 1 | 10635 | my $self = shift; | 
| 565 | 9 | 100 |  |  |  | 44 | @_ and $self->_set_attr_X ("verbatim", shift); | 
| 566 | 9 |  |  |  |  | 29 | $self->{'verbatim'}; | 
| 567 |  |  |  |  |  |  | } # verbatim | 
| 568 |  |  |  |  |  |  |  | 
| 569 |  |  |  |  |  |  | sub undef_str { | 
| 570 | 12 |  |  | 12 | 1 | 3184 | my $self = shift; | 
| 571 | 12 | 100 |  |  |  | 62 | if (@_) { | 
| 572 | 11 |  |  |  |  | 20 | my $v = shift; | 
| 573 | 11 | 100 |  |  |  | 44 | $self->{'undef_str'} = defined $v ? "$v" : undef; | 
| 574 | 11 |  |  |  |  | 46 | $self->_cache_set ($_cache_id{'undef_str'}, $self->{'undef_str'}); | 
| 575 |  |  |  |  |  |  | } | 
| 576 | 12 |  |  |  |  | 56 | $self->{'undef_str'}; | 
| 577 |  |  |  |  |  |  | } # undef_str | 
| 578 |  |  |  |  |  |  |  | 
| 579 |  |  |  |  |  |  | sub comment_str { | 
| 580 | 15 |  |  | 15 | 1 | 60 | my $self = shift; | 
| 581 | 15 | 100 |  |  |  | 34 | if (@_) { | 
| 582 | 14 |  |  |  |  | 20 | my $v = shift; | 
| 583 | 14 | 100 |  |  |  | 50 | $self->{'comment_str'} = defined $v ? "$v" : undef; | 
| 584 | 14 |  |  |  |  | 51 | $self->_cache_set ($_cache_id{'comment_str'}, $self->{'comment_str'}); | 
| 585 |  |  |  |  |  |  | } | 
| 586 | 15 |  |  |  |  | 35 | $self->{'comment_str'}; | 
| 587 |  |  |  |  |  |  | } # comment_str | 
| 588 |  |  |  |  |  |  |  | 
| 589 |  |  |  |  |  |  | sub auto_diag { | 
| 590 | 12 |  |  | 12 | 1 | 364 | my $self = shift; | 
| 591 | 12 | 100 |  |  |  | 37 | if (@_) { | 
| 592 | 9 |  |  |  |  | 33 | my $v = shift; | 
| 593 | 9 | 100 | 100 |  |  | 68 | !defined $v || $v eq "" and $v = 0; | 
| 594 | 9 | 100 |  |  |  | 56 | $v =~ m/^[0-9]/ or $v = lc $v eq "false" ? 0 : 1; # true/truth = 1 | 
|  |  | 100 |  |  |  |  |  | 
| 595 | 9 |  |  |  |  | 23 | $self->_set_attr_X ("auto_diag", $v); | 
| 596 |  |  |  |  |  |  | } | 
| 597 | 12 |  |  |  |  | 82 | $self->{'auto_diag'}; | 
| 598 |  |  |  |  |  |  | } # auto_diag | 
| 599 |  |  |  |  |  |  |  | 
| 600 |  |  |  |  |  |  | sub diag_verbose { | 
| 601 | 10 |  |  | 10 | 1 | 532 | my $self = shift; | 
| 602 | 10 | 100 |  |  |  | 35 | if (@_) { | 
| 603 | 8 |  |  |  |  | 12 | my $v = shift; | 
| 604 | 8 | 100 | 100 |  |  | 38 | !defined $v || $v eq "" and $v = 0; | 
| 605 | 8 | 100 |  |  |  | 39 | $v =~ m/^[0-9]/ or $v = lc $v eq "false" ? 0 : 1; # true/truth = 1 | 
|  |  | 100 |  |  |  |  |  | 
| 606 | 8 |  |  |  |  | 20 | $self->_set_attr_X ("diag_verbose", $v); | 
| 607 |  |  |  |  |  |  | } | 
| 608 | 10 |  |  |  |  | 59 | $self->{'diag_verbose'}; | 
| 609 |  |  |  |  |  |  | } # diag_verbose | 
| 610 |  |  |  |  |  |  |  | 
| 611 |  |  |  |  |  |  | # status | 
| 612 |  |  |  |  |  |  | # | 
| 613 |  |  |  |  |  |  | #   object method returning the success or failure of the most recent | 
| 614 |  |  |  |  |  |  | #   combine () or parse ().  there are no side-effects. | 
| 615 |  |  |  |  |  |  |  | 
| 616 |  |  |  |  |  |  | sub status { | 
| 617 | 5 |  |  | 5 | 1 | 11 | my $self = shift; | 
| 618 | 5 |  |  |  |  | 26 | return $self->{'_STATUS'}; | 
| 619 |  |  |  |  |  |  | } # status | 
| 620 |  |  |  |  |  |  |  | 
| 621 |  |  |  |  |  |  | sub eof { | 
| 622 | 33 |  |  | 33 | 1 | 151231 | my $self = shift; | 
| 623 | 33 |  |  |  |  | 141 | return $self->{'_EOF'}; | 
| 624 |  |  |  |  |  |  | } # status | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | sub types { | 
| 627 | 7 |  |  | 7 | 1 | 1591 | my $self = shift; | 
| 628 | 7 | 100 |  |  |  | 15 | if (@_) { | 
| 629 | 2 | 100 |  |  |  | 6 | if (my $types = shift) { | 
| 630 | 1 |  |  |  |  | 9 | $self->{'_types'} = join "", map { chr } @{$types}; | 
|  | 3 |  |  |  |  | 11 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 631 | 1 |  |  |  |  | 3 | $self->{'types'}  = $types; | 
| 632 | 1 |  |  |  |  | 7 | $self->_cache_set ($_cache_id{'types'}, $self->{'_types'}); | 
| 633 |  |  |  |  |  |  | } | 
| 634 |  |  |  |  |  |  | else { | 
| 635 | 1 |  |  |  |  | 2 | delete $self->{'types'}; | 
| 636 | 1 |  |  |  |  | 3 | delete $self->{'_types'}; | 
| 637 | 1 |  |  |  |  | 6 | $self->_cache_set ($_cache_id{'types'}, undef); | 
| 638 | 1 |  |  |  |  | 4 | undef; | 
| 639 |  |  |  |  |  |  | } | 
| 640 |  |  |  |  |  |  | } | 
| 641 |  |  |  |  |  |  | else { | 
| 642 | 5 |  |  |  |  | 22 | $self->{'types'}; | 
| 643 |  |  |  |  |  |  | } | 
| 644 |  |  |  |  |  |  | } # types | 
| 645 |  |  |  |  |  |  |  | 
| 646 |  |  |  |  |  |  | sub callbacks { | 
| 647 | 73 |  |  | 73 | 1 | 24075 | my $self = shift; | 
| 648 | 73 | 100 |  |  |  | 170 | if (@_) { | 
| 649 | 43 |  |  |  |  | 58 | my $cb; | 
| 650 | 43 |  |  |  |  | 68 | my $hf = 0x00; | 
| 651 | 43 | 100 |  |  |  | 92 | if (defined $_[0]) { | 
|  |  | 100 |  |  |  |  |  | 
| 652 | 41 | 100 |  |  |  | 71 | grep { !defined } @_ and croak ($self->SetDiag (1004)); | 
|  | 73 |  |  |  |  | 397 |  | 
| 653 | 39 | 100 | 100 |  |  | 990 | $cb = @_ == 1 && ref $_[0] eq "HASH" ? shift | 
|  |  | 100 |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | : @_ % 2 == 0                    ? { @_ } | 
| 655 |  |  |  |  |  |  | : croak ($self->SetDiag (1004)); | 
| 656 | 34 |  |  |  |  | 53 | foreach my $cbk (keys %{$cb}) { | 
|  | 34 |  |  |  |  | 100 |  | 
| 657 |  |  |  |  |  |  | # A key cannot be a ref. That would be stored as the *string | 
| 658 |  |  |  |  |  |  | # 'SCALAR(0x1f3e710)' or 'ARRAY(0x1a5ae18)' | 
| 659 | 36 | 100 | 100 |  |  | 1468 | $cbk =~ m/^[\w.]+$/ && ref $cb->{$cbk} eq "CODE" or | 
| 660 |  |  |  |  |  |  | croak ($self->SetDiag (1004)); | 
| 661 |  |  |  |  |  |  | } | 
| 662 | 20 | 100 |  |  |  | 51 | exists $cb->{'error'}        and $hf |= 0x01; | 
| 663 | 20 | 100 |  |  |  | 45 | exists $cb->{'after_parse'}  and $hf |= 0x02; | 
| 664 | 20 | 100 |  |  |  | 39 | exists $cb->{'before_print'} and $hf |= 0x04; | 
| 665 |  |  |  |  |  |  | } | 
| 666 |  |  |  |  |  |  | elsif (@_ > 1) { | 
| 667 |  |  |  |  |  |  | # (undef, whatever) | 
| 668 | 1 |  |  |  |  | 91 | croak ($self->SetDiag (1004)); | 
| 669 |  |  |  |  |  |  | } | 
| 670 | 21 |  |  |  |  | 56 | $self->_set_attr_X ("_has_hooks", $hf); | 
| 671 | 21 |  |  |  |  | 51 | $self->{'callbacks'} = $cb; | 
| 672 |  |  |  |  |  |  | } | 
| 673 | 51 |  |  |  |  | 140 | $self->{'callbacks'}; | 
| 674 |  |  |  |  |  |  | } # callbacks | 
| 675 |  |  |  |  |  |  |  | 
| 676 |  |  |  |  |  |  | # error_diag | 
| 677 |  |  |  |  |  |  | # | 
| 678 |  |  |  |  |  |  | #   If (and only if) an error occurred, this function returns a code that | 
| 679 |  |  |  |  |  |  | #   indicates the reason of failure | 
| 680 |  |  |  |  |  |  |  | 
| 681 |  |  |  |  |  |  | sub error_diag { | 
| 682 | 1710 |  |  | 1710 | 1 | 146306 | my $self = shift; | 
| 683 | 1710 |  |  |  |  | 4606 | my @diag = (0 + $last_new_err, $last_new_err, 0, 0, 0); | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  | # Docs state to NEVER use UNIVERSAL::isa, because it will *never* call an | 
| 686 |  |  |  |  |  |  | # overridden isa method in any class. Well, that is exacly what I want here | 
| 687 | 1710 | 100 | 100 |  |  | 14058 | if ($self && ref $self and # Not a class method or direct call | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 688 |  |  |  |  |  |  | UNIVERSAL::isa ($self, __PACKAGE__) && exists $self->{'_ERROR_DIAG'}) { | 
| 689 | 1535 |  |  |  |  | 3029 | $diag[0] = 0 + $self->{'_ERROR_DIAG'}; | 
| 690 | 1535 |  |  |  |  | 2565 | $diag[1] =     $self->{'_ERROR_DIAG'}; | 
| 691 | 1535 | 100 |  |  |  | 3134 | $diag[2] = 1 + $self->{'_ERROR_POS'} if exists $self->{'_ERROR_POS'}; | 
| 692 | 1535 |  |  |  |  | 2259 | $diag[3] =     $self->{'_RECNO'}; | 
| 693 | 1535 | 100 |  |  |  | 2874 | $diag[4] =     $self->{'_ERROR_FLD'} if exists $self->{'_ERROR_FLD'}; | 
| 694 |  |  |  |  |  |  |  | 
| 695 |  |  |  |  |  |  | $diag[0] && $self->{'callbacks'} && $self->{'callbacks'}{'error'} and | 
| 696 | 1535 | 100 | 100 |  |  | 6100 | return $self->{'callbacks'}{'error'}->(@diag); | 
|  |  |  | 100 |  |  |  |  | 
| 697 |  |  |  |  |  |  | } | 
| 698 |  |  |  |  |  |  |  | 
| 699 | 1701 |  |  |  |  | 3044 | my $context = wantarray; | 
| 700 | 1701 | 100 |  |  |  | 3340 | unless (defined $context) {	# Void context, auto-diag | 
| 701 | 281 | 100 | 100 |  |  | 1307 | if ($diag[0] && $diag[0] != 2012) { | 
| 702 | 19 |  |  |  |  | 131 | my $msg = "# CSV_XS ERROR: $diag[0] - $diag[1] \@ rec $diag[3] pos $diag[2]\n"; | 
| 703 | 19 | 100 |  |  |  | 102 | $diag[4] and $msg =~ s/$/ field $diag[4]/; | 
| 704 |  |  |  |  |  |  |  | 
| 705 | 19 | 100 | 100 |  |  | 88 | unless ($self && ref $self) {	# auto_diag | 
| 706 |  |  |  |  |  |  | # called without args in void context | 
| 707 | 4 |  |  |  |  | 43 | warn $msg; | 
| 708 | 4 |  |  |  |  | 32 | return; | 
| 709 |  |  |  |  |  |  | } | 
| 710 |  |  |  |  |  |  |  | 
| 711 |  |  |  |  |  |  | $self->{'diag_verbose'} && $self->{'_ERROR_INPUT'} and | 
| 712 | 15 | 50 | 66 |  |  | 55 | $msg .= $self->{'_ERROR_INPUT'}."\n". | 
| 713 |  |  |  |  |  |  | (" " x ($diag[2] - 1))."^\n"; | 
| 714 |  |  |  |  |  |  |  | 
| 715 | 15 |  |  |  |  | 29 | my $lvl = $self->{'auto_diag'}; | 
| 716 | 15 | 100 |  |  |  | 51 | if ($lvl < 2) { | 
| 717 | 12 |  |  |  |  | 77 | my @c = caller (2); | 
| 718 | 12 | 50 | 66 |  |  | 78 | if (@c >= 11 && $c[10] && ref $c[10] eq "HASH") { | 
|  |  |  | 33 |  |  |  |  | 
| 719 | 0 |  |  |  |  | 0 | my $hints = $c[10]; | 
| 720 |  |  |  |  |  |  | (exists $hints->{'autodie'} && $hints->{'autodie'} or | 
| 721 |  |  |  |  |  |  | exists $hints->{'guard Fatal'} && | 
| 722 | 0 | 0 | 0 |  |  | 0 | !exists $hints->{'no Fatal'}) and | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 0 |  |  |  |  | 
| 723 |  |  |  |  |  |  | $lvl++; | 
| 724 |  |  |  |  |  |  | # Future releases of autodie will probably set $^H{autodie} | 
| 725 |  |  |  |  |  |  | #  to "autodie @args", like "autodie :all" or "autodie open" | 
| 726 |  |  |  |  |  |  | #  so we can/should check for "open" or "new" | 
| 727 |  |  |  |  |  |  | } | 
| 728 |  |  |  |  |  |  | } | 
| 729 | 15 | 100 |  |  |  | 142 | $lvl > 1 ? die $msg : warn $msg; | 
| 730 |  |  |  |  |  |  | } | 
| 731 | 274 |  |  |  |  | 2274 | return; | 
| 732 |  |  |  |  |  |  | } | 
| 733 | 1420 | 100 |  |  |  | 6009 | return $context ? @diag : $diag[1]; | 
| 734 |  |  |  |  |  |  | } # error_diag | 
| 735 |  |  |  |  |  |  |  | 
| 736 |  |  |  |  |  |  | sub record_number { | 
| 737 | 3 |  |  | 3 | 1 | 5 | my $self = shift; | 
| 738 | 3 |  |  |  |  | 12 | return $self->{'_RECNO'}; | 
| 739 |  |  |  |  |  |  | } # record_number | 
| 740 |  |  |  |  |  |  |  | 
| 741 |  |  |  |  |  |  | # string | 
| 742 |  |  |  |  |  |  | # | 
| 743 |  |  |  |  |  |  | #   object method returning the result of the most recent combine () or the | 
| 744 |  |  |  |  |  |  | #   input to the most recent parse (), whichever is more recent.  there are | 
| 745 |  |  |  |  |  |  | #   no side-effects. | 
| 746 |  |  |  |  |  |  |  | 
| 747 |  |  |  |  |  |  | sub string { | 
| 748 | 1398 |  |  | 1398 | 1 | 354949 | my $self = shift; | 
| 749 | 1398 | 100 |  |  |  | 4260 | return ref $self->{'_STRING'} ? ${$self->{'_STRING'}} : undef; | 
|  | 1397 |  |  |  |  | 5632 |  | 
| 750 |  |  |  |  |  |  | } # string | 
| 751 |  |  |  |  |  |  |  | 
| 752 |  |  |  |  |  |  | # fields | 
| 753 |  |  |  |  |  |  | # | 
| 754 |  |  |  |  |  |  | #   object method returning the result of the most recent parse () or the | 
| 755 |  |  |  |  |  |  | #   input to the most recent combine (), whichever is more recent.  there | 
| 756 |  |  |  |  |  |  | #   are no side-effects. | 
| 757 |  |  |  |  |  |  |  | 
| 758 |  |  |  |  |  |  | sub fields { | 
| 759 | 1600 |  |  | 1600 | 1 | 19460 | my $self = shift; | 
| 760 | 1600 | 100 |  |  |  | 4290 | return ref $self->{'_FIELDS'} ? @{$self->{'_FIELDS'}} : undef; | 
|  | 1599 |  |  |  |  | 9815 |  | 
| 761 |  |  |  |  |  |  | } # fields | 
| 762 |  |  |  |  |  |  |  | 
| 763 |  |  |  |  |  |  | # meta_info | 
| 764 |  |  |  |  |  |  | # | 
| 765 |  |  |  |  |  |  | #   object method returning the result of the most recent parse () or the | 
| 766 |  |  |  |  |  |  | #   input to the most recent combine (), whichever is more recent.  there | 
| 767 |  |  |  |  |  |  | #   are no side-effects. meta_info () returns (if available)  some of the | 
| 768 |  |  |  |  |  |  | #   field's properties | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | sub meta_info { | 
| 771 | 21 |  |  | 21 | 1 | 609 | my $self = shift; | 
| 772 | 21 | 100 |  |  |  | 65 | return ref $self->{'_FFLAGS'} ? @{$self->{'_FFLAGS'}} : undef; | 
|  | 16 |  |  |  |  | 72 |  | 
| 773 |  |  |  |  |  |  | } # meta_info | 
| 774 |  |  |  |  |  |  |  | 
| 775 |  |  |  |  |  |  | sub is_quoted { | 
| 776 | 12 |  |  | 12 | 1 | 18101 | my ($self, $idx) = @_; | 
| 777 |  |  |  |  |  |  | ref $self->{'_FFLAGS'} && | 
| 778 | 12 | 100 | 100 |  |  | 89 | $idx >= 0 && $idx < @{$self->{'_FFLAGS'}} or return; | 
|  | 8 |  | 100 |  |  | 31 |  | 
| 779 | 7 | 100 |  |  |  | 28 | $self->{'_FFLAGS'}[$idx] & CSV_FLAGS_IS_QUOTED  () ? 1 : 0; | 
| 780 |  |  |  |  |  |  | } # is_quoted | 
| 781 |  |  |  |  |  |  |  | 
| 782 |  |  |  |  |  |  | sub is_binary { | 
| 783 | 11 |  |  | 11 | 1 | 1029 | my ($self, $idx) = @_; | 
| 784 |  |  |  |  |  |  | ref $self->{'_FFLAGS'} && | 
| 785 | 11 | 100 | 100 |  |  | 74 | $idx >= 0 && $idx < @{$self->{'_FFLAGS'}} or return; | 
|  | 9 |  | 100 |  |  | 38 |  | 
| 786 | 8 | 100 |  |  |  | 25 | $self->{'_FFLAGS'}[$idx] & CSV_FLAGS_IS_BINARY  () ? 1 : 0; | 
| 787 |  |  |  |  |  |  | } # is_binary | 
| 788 |  |  |  |  |  |  |  | 
| 789 |  |  |  |  |  |  | sub is_missing { | 
| 790 | 19 |  |  | 19 | 1 | 44 | my ($self, $idx) = @_; | 
| 791 | 19 | 100 | 100 |  |  | 134 | $idx < 0 || !ref $self->{'_FFLAGS'} and return; | 
| 792 | 11 | 100 |  |  |  | 18 | $idx >= @{$self->{'_FFLAGS'}} and return 1; | 
|  | 11 |  |  |  |  | 33 |  | 
| 793 | 10 | 100 |  |  |  | 24 | $self->{'_FFLAGS'}[$idx] & CSV_FLAGS_IS_MISSING () ? 1 : 0; | 
| 794 |  |  |  |  |  |  | } # is_missing | 
| 795 |  |  |  |  |  |  |  | 
| 796 |  |  |  |  |  |  | # combine | 
| 797 |  |  |  |  |  |  | # | 
| 798 |  |  |  |  |  |  | #  Object method returning success or failure. The given arguments are | 
| 799 |  |  |  |  |  |  | #  combined into a single comma-separated value. Failure can be the | 
| 800 |  |  |  |  |  |  | #  result of no arguments or an argument containing an invalid character. | 
| 801 |  |  |  |  |  |  | #  side-effects include: | 
| 802 |  |  |  |  |  |  | #      setting status () | 
| 803 |  |  |  |  |  |  | #      setting fields () | 
| 804 |  |  |  |  |  |  | #      setting string () | 
| 805 |  |  |  |  |  |  | #      setting error_input () | 
| 806 |  |  |  |  |  |  |  | 
| 807 |  |  |  |  |  |  | sub combine { | 
| 808 | 1397 |  |  | 1397 | 1 | 679872 | my $self = shift; | 
| 809 | 1397 |  |  |  |  | 2726 | my $str  = ""; | 
| 810 | 1397 |  |  |  |  | 4615 | $self->{'_FIELDS'} = \@_; | 
| 811 | 1397 |  | 100 |  |  | 22344 | $self->{'_STATUS'} = (@_ > 0) && $self->Combine (\$str, \@_, 0); | 
| 812 | 1393 |  |  |  |  | 3469 | $self->{'_STRING'} = \$str; | 
| 813 | 1393 |  |  |  |  | 4709 | $self->{'_STATUS'}; | 
| 814 |  |  |  |  |  |  | } # combine | 
| 815 |  |  |  |  |  |  |  | 
| 816 |  |  |  |  |  |  | # parse | 
| 817 |  |  |  |  |  |  | # | 
| 818 |  |  |  |  |  |  | #  Object method returning success or failure. The given argument is | 
| 819 |  |  |  |  |  |  | #  expected to be a valid comma-separated value. Failure can be the | 
| 820 |  |  |  |  |  |  | #  result of no arguments or an argument containing an invalid sequence | 
| 821 |  |  |  |  |  |  | #  of characters. Side-effects include: | 
| 822 |  |  |  |  |  |  | #      setting status () | 
| 823 |  |  |  |  |  |  | #      setting fields () | 
| 824 |  |  |  |  |  |  | #      setting meta_info () | 
| 825 |  |  |  |  |  |  | #      setting string () | 
| 826 |  |  |  |  |  |  | #      setting error_input () | 
| 827 |  |  |  |  |  |  |  | 
| 828 |  |  |  |  |  |  | sub parse { | 
| 829 | 1938 |  |  | 1938 | 1 | 107057 | my ($self, $str) = @_; | 
| 830 |  |  |  |  |  |  |  | 
| 831 | 1938 | 100 |  |  |  | 4730 | ref $str and croak ($self->SetDiag (1500)); | 
| 832 |  |  |  |  |  |  |  | 
| 833 | 1934 |  |  |  |  | 3147 | my $fields = []; | 
| 834 | 1934 |  |  |  |  | 2955 | my $fflags = []; | 
| 835 | 1934 |  |  |  |  | 4191 | $self->{'_STRING'} = \$str; | 
| 836 | 1934 | 100 | 100 |  |  | 33663 | if (defined $str && $self->Parse ($str, $fields, $fflags)) { | 
| 837 | 1724 |  |  |  |  | 4544 | $self->{'_FIELDS'} = $fields; | 
| 838 | 1724 |  |  |  |  | 2522 | $self->{'_FFLAGS'} = $fflags; | 
| 839 | 1724 |  |  |  |  | 2794 | $self->{'_STATUS'} = 1; | 
| 840 |  |  |  |  |  |  | } | 
| 841 |  |  |  |  |  |  | else { | 
| 842 | 207 |  |  |  |  | 475 | $self->{'_FIELDS'} = undef; | 
| 843 | 207 |  |  |  |  | 301 | $self->{'_FFLAGS'} = undef; | 
| 844 | 207 |  |  |  |  | 303 | $self->{'_STATUS'} = 0; | 
| 845 |  |  |  |  |  |  | } | 
| 846 | 1931 |  |  |  |  | 7523 | $self->{'_STATUS'}; | 
| 847 |  |  |  |  |  |  | } # parse | 
| 848 |  |  |  |  |  |  |  | 
| 849 |  |  |  |  |  |  | sub column_names { | 
| 850 | 982 |  |  | 982 | 1 | 72695 | my ($self, @keys) = @_; | 
| 851 |  |  |  |  |  |  | @keys or | 
| 852 | 982 | 100 |  |  |  | 2415 | return defined $self->{'_COLUMN_NAMES'} ? @{$self->{'_COLUMN_NAMES'}} : (); | 
|  | 282 | 100 |  |  |  | 1191 |  | 
| 853 |  |  |  |  |  |  |  | 
| 854 |  |  |  |  |  |  | @keys == 1 && ! defined $keys[0] and | 
| 855 | 657 | 100 | 100 |  |  | 2386 | return $self->{'_COLUMN_NAMES'} = undef; | 
| 856 |  |  |  |  |  |  |  | 
| 857 | 519 | 100 | 100 |  |  | 1684 | if (@keys == 1 && ref $keys[0] eq "ARRAY") { | 
|  |  | 100 |  |  |  |  |  | 
| 858 | 211 |  |  |  |  | 280 | @keys = @{$keys[0]}; | 
|  | 211 |  |  |  |  | 500 |  | 
| 859 |  |  |  |  |  |  | } | 
| 860 | 655 | 100 |  |  |  | 2004 | elsif (join "", map { defined $_ ? ref $_ : "" } @keys) { | 
| 861 | 5 |  |  |  |  | 605 | croak ($self->SetDiag (3001)); | 
| 862 |  |  |  |  |  |  | } | 
| 863 |  |  |  |  |  |  |  | 
| 864 | 514 | 100 | 100 |  |  | 1305 | $self->{'_BOUND_COLUMNS'} && @keys != @{$self->{'_BOUND_COLUMNS'}} and | 
|  | 2 |  |  |  |  | 97 |  | 
| 865 |  |  |  |  |  |  | croak ($self->SetDiag (3003)); | 
| 866 |  |  |  |  |  |  |  | 
| 867 | 513 | 100 |  |  |  | 842 | $self->{'_COLUMN_NAMES'} = [ map { defined $_ ? $_ : "\cAUNDEF\cA" } @keys ]; | 
|  | 1153 |  |  |  |  | 2911 |  | 
| 868 | 513 |  |  |  |  | 796 | @{$self->{'_COLUMN_NAMES'}}; | 
|  | 513 |  |  |  |  | 1248 |  | 
| 869 |  |  |  |  |  |  | } # column_names | 
| 870 |  |  |  |  |  |  |  | 
| 871 |  |  |  |  |  |  | sub header { | 
| 872 | 320 |  |  | 320 | 1 | 39105 | my ($self, $fh, @args) = @_; | 
| 873 |  |  |  |  |  |  |  | 
| 874 | 320 | 100 |  |  |  | 907 | $fh or croak ($self->SetDiag (1014)); | 
| 875 |  |  |  |  |  |  |  | 
| 876 | 319 |  |  |  |  | 484 | my (@seps, %args); | 
| 877 | 319 |  |  |  |  | 647 | for (@args) { | 
| 878 | 212 | 100 |  |  |  | 472 | if (ref $_ eq "ARRAY") { | 
| 879 | 18 |  |  |  |  | 27 | push @seps, @{$_}; | 
|  | 18 |  |  |  |  | 48 |  | 
| 880 | 18 |  |  |  |  | 37 | next; | 
| 881 |  |  |  |  |  |  | } | 
| 882 | 194 | 100 |  |  |  | 408 | if (ref $_ eq "HASH") { | 
| 883 | 193 |  |  |  |  | 242 | %args = %{$_}; | 
|  | 193 |  |  |  |  | 443 |  | 
| 884 | 193 |  |  |  |  | 403 | next; | 
| 885 |  |  |  |  |  |  | } | 
| 886 | 1 |  |  |  |  | 87 | croak ('usage: $csv->header ($fh, [ seps ], { options })'); | 
| 887 |  |  |  |  |  |  | } | 
| 888 |  |  |  |  |  |  |  | 
| 889 |  |  |  |  |  |  | defined $args{'munge'} && !defined $args{'munge_column_names'} and | 
| 890 | 318 | 100 | 66 |  |  | 779 | $args{'munge_column_names'} = $args{'munge'}; # munge as alias | 
| 891 | 318 | 100 |  |  |  | 780 | defined $args{'detect_bom'}         or $args{'detect_bom'}         = 1; | 
| 892 | 318 | 100 |  |  |  | 698 | defined $args{'set_column_names'}   or $args{'set_column_names'}   = 1; | 
| 893 | 318 | 100 |  |  |  | 690 | defined $args{'munge_column_names'} or $args{'munge_column_names'} = "lc"; | 
| 894 |  |  |  |  |  |  |  | 
| 895 |  |  |  |  |  |  | # Reset any previous leftovers | 
| 896 | 318 |  |  |  |  | 498 | $self->{'_RECNO'}		= 0; | 
| 897 | 318 |  |  |  |  | 461 | $self->{'_AHEAD'}		= undef; | 
| 898 | 318 | 100 |  |  |  | 674 | $self->{'_COLUMN_NAMES'}	= undef if $args{'set_column_names'}; | 
| 899 | 318 | 100 |  |  |  | 614 | $self->{'_BOUND_COLUMNS'}	= undef if $args{'set_column_names'}; | 
| 900 |  |  |  |  |  |  |  | 
| 901 | 318 | 100 |  |  |  | 631 | if (defined   $args{'sep_set'}) { | 
| 902 | 27 | 100 |  |  |  | 72 | ref       $args{'sep_set'} eq "ARRAY" or | 
| 903 |  |  |  |  |  |  | croak ($self->_SetDiagInfo (1500, "sep_set should be an array ref")); | 
| 904 | 22 |  |  |  |  | 33 | @seps = @{$args{'sep_set'}}; | 
|  | 22 |  |  |  |  | 46 |  | 
| 905 |  |  |  |  |  |  | } | 
| 906 |  |  |  |  |  |  |  | 
| 907 | 313 | 50 |  |  |  | 917 | $^O eq "MSWin32" and binmode $fh; | 
| 908 | 313 |  |  |  |  | 5346 | my $hdr = <$fh>; | 
| 909 |  |  |  |  |  |  | # check if $hdr can be empty here, I don't think so | 
| 910 | 313 | 100 | 66 |  |  | 2109 | defined $hdr && $hdr ne "" or croak ($self->SetDiag (1010)); | 
| 911 |  |  |  |  |  |  |  | 
| 912 | 311 |  |  |  |  | 463 | my %sep; | 
| 913 | 311 | 100 |  |  |  | 961 | @seps or @seps = (",", ";"); | 
| 914 | 311 |  |  |  |  | 609 | foreach my $sep (@seps) { | 
| 915 | 706 | 100 |  |  |  | 2007 | index ($hdr, $sep) >= 0 and $sep{$sep}++; | 
| 916 |  |  |  |  |  |  | } | 
| 917 |  |  |  |  |  |  |  | 
| 918 | 311 | 100 |  |  |  | 824 | keys %sep >= 2 and croak ($self->SetDiag (1011)); | 
| 919 |  |  |  |  |  |  |  | 
| 920 | 307 |  |  |  |  | 1111 | $self->sep (keys %sep); | 
| 921 | 307 |  |  |  |  | 520 | my $enc = ""; | 
| 922 | 307 | 100 |  |  |  | 650 | if ($args{'detect_bom'}) { # UTF-7 is not supported | 
| 923 | 306 | 100 |  |  |  | 2670 | if ($hdr =~ s/^\x00\x00\xfe\xff//) { $enc = "utf-32be"   } | 
|  | 24 | 100 |  |  |  | 50 |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 924 | 24 |  |  |  |  | 49 | elsif ($hdr =~ s/^\xff\xfe\x00\x00//) { $enc = "utf-32le"   } | 
| 925 | 25 |  |  |  |  | 58 | elsif ($hdr =~ s/^\xfe\xff//)         { $enc = "utf-16be"   } | 
| 926 | 24 |  |  |  |  | 69 | elsif ($hdr =~ s/^\xff\xfe//)         { $enc = "utf-16le"   } | 
| 927 | 48 |  |  |  |  | 87 | elsif ($hdr =~ s/^\xef\xbb\xbf//)     { $enc = "utf-8"      } | 
| 928 | 1 |  |  |  |  | 3 | elsif ($hdr =~ s/^\xf7\x64\x4c//)     { $enc = "utf-1"      } | 
| 929 | 1 |  |  |  |  | 3 | elsif ($hdr =~ s/^\xdd\x73\x66\x73//) { $enc = "utf-ebcdic" } | 
| 930 | 1 |  |  |  |  | 2 | elsif ($hdr =~ s/^\x0e\xfe\xff//)     { $enc = "scsu"       } | 
| 931 | 1 |  |  |  |  | 3 | elsif ($hdr =~ s/^\xfb\xee\x28//)     { $enc = "bocu-1"     } | 
| 932 | 1 |  |  |  |  | 3 | elsif ($hdr =~ s/^\x84\x31\x95\x33//) { $enc = "gb-18030"   } | 
| 933 | 36 |  |  |  |  | 58 | elsif ($hdr =~ s/^\x{feff}//)         { $enc = ""           } | 
| 934 |  |  |  |  |  |  |  | 
| 935 | 306 | 100 |  |  |  | 754 | $self->{'ENCODING'} = $enc ? uc $enc : undef; | 
| 936 |  |  |  |  |  |  |  | 
| 937 | 306 | 100 |  |  |  | 1171 | $hdr eq "" and croak ($self->SetDiag (1010)); | 
| 938 |  |  |  |  |  |  |  | 
| 939 | 300 | 100 |  |  |  | 566 | if ($enc) { | 
| 940 | 144 | 50 | 33 |  |  | 330 | $ebcdic && $enc eq "utf-ebcdic" and $enc = ""; | 
| 941 | 144 | 100 |  |  |  | 403 | if ($enc =~ m/([13]).le$/) { | 
| 942 | 48 |  |  |  |  | 184 | my $l = 0 + $1; | 
| 943 | 48 |  |  |  |  | 87 | my $x; | 
| 944 | 48 |  |  |  |  | 121 | $hdr .= "\0" x $l; | 
| 945 | 48 |  |  |  |  | 165 | read $fh, $x, $l; | 
| 946 |  |  |  |  |  |  | } | 
| 947 | 144 | 50 |  |  |  | 291 | if ($enc) { | 
| 948 | 144 | 100 |  |  |  | 288 | if ($enc ne "utf-8") { | 
| 949 | 96 |  |  |  |  | 580 | require Encode; | 
| 950 | 96 |  |  |  |  | 544 | $hdr = Encode::decode ($enc, $hdr); | 
| 951 |  |  |  |  |  |  | } | 
| 952 | 144 |  |  |  |  | 5509 | binmode $fh, ":encoding($enc)"; | 
| 953 |  |  |  |  |  |  | } | 
| 954 |  |  |  |  |  |  | } | 
| 955 |  |  |  |  |  |  | } | 
| 956 |  |  |  |  |  |  |  | 
| 957 | 301 |  |  |  |  | 8428 | my ($ahead, $eol); | 
| 958 | 301 | 100 | 66 |  |  | 1569 | if ($hdr and $hdr =~ s/\Asep=(\S)([\r\n]+)//i) { # Also look in xs:Parse | 
| 959 | 1 |  |  |  |  | 9 | $self->sep ($1); | 
| 960 | 1 | 50 |  |  |  | 6 | length $hdr or $hdr = <$fh>; | 
| 961 |  |  |  |  |  |  | } | 
| 962 | 301 | 100 |  |  |  | 1973 | if ($hdr =~ s/^([^\r\n]+)([\r\n]+)([^\r\n].+)\z/$1/s) { | 
| 963 | 142 |  |  |  |  | 289 | $eol   = $2; | 
| 964 | 142 |  |  |  |  | 317 | $ahead = $3; | 
| 965 |  |  |  |  |  |  | } | 
| 966 |  |  |  |  |  |  |  | 
| 967 | 301 |  |  |  |  | 509 | my $hr = \$hdr; # Will cause croak on perl-5.6.x | 
| 968 | 301 | 50 |  |  |  | 3144 | open my $h, "<", $hr or croak ($self->SetDiag (1010)); | 
| 969 |  |  |  |  |  |  |  | 
| 970 | 301 | 100 |  |  |  | 11644 | my $row = $self->getline ($h) or croak (); | 
| 971 | 299 |  |  |  |  | 12608 | close $h; | 
| 972 |  |  |  |  |  |  |  | 
| 973 | 299 | 100 |  |  |  | 829 | if (   $args{'munge_column_names'} eq "lc") { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 974 | 280 |  |  |  |  | 369 | $_ = lc for @{$row}; | 
|  | 280 |  |  |  |  | 978 |  | 
| 975 |  |  |  |  |  |  | } | 
| 976 |  |  |  |  |  |  | elsif ($args{'munge_column_names'} eq "uc") { | 
| 977 | 7 |  |  |  |  | 15 | $_ = uc for @{$row}; | 
|  | 7 |  |  |  |  | 46 |  | 
| 978 |  |  |  |  |  |  | } | 
| 979 |  |  |  |  |  |  | elsif ($args{'munge_column_names'} eq "db") { | 
| 980 | 3 |  |  |  |  | 9 | for (@{$row}) { | 
|  | 3 |  |  |  |  | 9 |  | 
| 981 | 7 |  |  |  |  | 15 | s/\W+/_/g; | 
| 982 | 7 |  |  |  |  | 16 | s/^_+//; | 
| 983 | 7 |  |  |  |  | 14 | $_ = lc; | 
| 984 |  |  |  |  |  |  | } | 
| 985 |  |  |  |  |  |  | } | 
| 986 |  |  |  |  |  |  |  | 
| 987 | 299 | 100 |  |  |  | 651 | if ($ahead) { # Must be after getline, which creates the cache | 
| 988 | 142 |  |  |  |  | 560 | $self->_cache_set ($_cache_id{'_has_ahead'}, 1); | 
| 989 | 142 |  |  |  |  | 231 | $self->{'_AHEAD'} = $ahead; | 
| 990 | 142 | 100 |  |  |  | 559 | $eol =~ m/^\r([^\n]|\z)/ and $self->eol ($eol); | 
| 991 |  |  |  |  |  |  | } | 
| 992 |  |  |  |  |  |  |  | 
| 993 | 299 |  |  |  |  | 374 | my @hdr = @{$row}; | 
|  | 299 |  |  |  |  | 807 |  | 
| 994 |  |  |  |  |  |  | ref $args{'munge_column_names'} eq "CODE" and | 
| 995 | 299 | 100 |  |  |  | 802 | @hdr = map { $args{'munge_column_names'}->($_)       } @hdr; | 
|  | 4 |  |  |  |  | 19 |  | 
| 996 |  |  |  |  |  |  | ref $args{'munge_column_names'} eq "HASH" and | 
| 997 | 299 | 100 |  |  |  | 582 | @hdr = map { $args{'munge_column_names'}->{$_} || $_ } @hdr; | 
|  | 3 | 100 |  |  |  | 16 |  | 
| 998 | 299 |  |  |  |  | 416 | my %hdr; $hdr{$_}++ for @hdr; | 
|  | 299 |  |  |  |  | 1076 |  | 
| 999 | 299 | 100 |  |  |  | 759 | exists $hdr{""} and croak ($self->SetDiag (1012)); | 
| 1000 | 297 | 100 |  |  |  | 792 | unless (keys %hdr == @hdr) { | 
| 1001 |  |  |  |  |  |  | croak ($self->_SetDiagInfo (1013, join ", " => | 
| 1002 | 1 |  |  |  |  | 7 | map { "$_ ($hdr{$_})" } grep { $hdr{$_} > 1 } keys %hdr)); | 
|  | 1 |  |  |  |  | 9 |  | 
|  | 2 |  |  |  |  | 7 |  | 
| 1003 |  |  |  |  |  |  | } | 
| 1004 | 296 | 100 |  |  |  | 970 | $args{'set_column_names'} and $self->column_names (@hdr); | 
| 1005 | 296 | 100 |  |  |  | 2488 | wantarray ? @hdr : $self; | 
| 1006 |  |  |  |  |  |  | } # header | 
| 1007 |  |  |  |  |  |  |  | 
| 1008 |  |  |  |  |  |  | sub bind_columns { | 
| 1009 | 27 |  |  | 27 | 1 | 21233 | my ($self, @refs) = @_; | 
| 1010 |  |  |  |  |  |  | @refs or | 
| 1011 | 27 | 100 |  |  |  | 110 | return defined $self->{'_BOUND_COLUMNS'} ? @{$self->{'_BOUND_COLUMNS'}} : undef; | 
|  | 2 | 100 |  |  |  | 13 |  | 
| 1012 |  |  |  |  |  |  |  | 
| 1013 | 23 | 100 | 100 |  |  | 119 | if (@refs == 1 && ! defined $refs[0]) { | 
| 1014 | 5 |  |  |  |  | 14 | $self->{'_COLUMN_NAMES'} = undef; | 
| 1015 | 5 |  |  |  |  | 27 | return $self->{'_BOUND_COLUMNS'} = undef; | 
| 1016 |  |  |  |  |  |  | } | 
| 1017 |  |  |  |  |  |  |  | 
| 1018 | 18 | 100 | 100 |  |  | 70 | $self->{'_COLUMN_NAMES'} && @refs != @{$self->{'_COLUMN_NAMES'}} and | 
|  | 3 |  |  |  |  | 449 |  | 
| 1019 |  |  |  |  |  |  | croak ($self->SetDiag (3003)); | 
| 1020 |  |  |  |  |  |  |  | 
| 1021 | 17 | 100 |  |  |  | 196 | join "", map { ref $_ eq "SCALAR" ? "" : "*" } @refs and | 
|  | 74606 | 100 |  |  |  | 139475 |  | 
| 1022 |  |  |  |  |  |  | croak ($self->SetDiag (3004)); | 
| 1023 |  |  |  |  |  |  |  | 
| 1024 | 15 |  |  |  |  | 3236 | $self->_set_attr_N ("_is_bound", scalar @refs); | 
| 1025 | 15 |  |  |  |  | 4261 | $self->{'_BOUND_COLUMNS'} = [ @refs ]; | 
| 1026 | 15 |  |  |  |  | 1403 | @refs; | 
| 1027 |  |  |  |  |  |  | } # bind_columns | 
| 1028 |  |  |  |  |  |  |  | 
| 1029 |  |  |  |  |  |  | sub getline_hr { | 
| 1030 | 119 |  |  | 119 | 1 | 11829 | my ($self, @args, %hr) = @_; | 
| 1031 | 119 | 100 |  |  |  | 430 | $self->{'_COLUMN_NAMES'} or croak ($self->SetDiag (3002)); | 
| 1032 | 118 | 100 |  |  |  | 2454 | my $fr = $self->getline (@args) or return; | 
| 1033 | 116 | 100 |  |  |  | 2278 | if (ref $self->{'_FFLAGS'}) { # missing | 
| 1034 |  |  |  |  |  |  | $self->{'_FFLAGS'}[$_] = CSV_FLAGS_IS_MISSING () | 
| 1035 | 5 | 50 |  |  |  | 12 | for (@{$fr} ? $#{$fr} + 1 : 0) .. $#{$self->{'_COLUMN_NAMES'}}; | 
|  | 5 |  |  |  |  | 14 |  | 
|  | 5 |  |  |  |  | 12 |  | 
|  | 5 |  |  |  |  | 22 |  | 
| 1036 | 5 |  |  |  |  | 33 | @{$fr} == 1 && (!defined $fr->[0] || $fr->[0] eq "") and | 
| 1037 | 5 | 100 | 33 |  |  | 9 | $self->{'_FFLAGS'}[0] ||= CSV_FLAGS_IS_MISSING (); | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 1038 |  |  |  |  |  |  | } | 
| 1039 | 116 |  |  |  |  | 167 | @hr{@{$self->{'_COLUMN_NAMES'}}} = @{$fr}; | 
|  | 116 |  |  |  |  | 414 |  | 
|  | 116 |  |  |  |  | 201 |  | 
| 1040 | 116 |  |  |  |  | 560 | \%hr; | 
| 1041 |  |  |  |  |  |  | } # getline_hr | 
| 1042 |  |  |  |  |  |  |  | 
| 1043 |  |  |  |  |  |  | sub getline_hr_all { | 
| 1044 | 235 |  |  | 235 | 1 | 439 | my ($self, @args) = @_; | 
| 1045 | 235 | 100 |  |  |  | 891 | $self->{'_COLUMN_NAMES'} or croak ($self->SetDiag (3002)); | 
| 1046 | 233 |  |  |  |  | 299 | my @cn = @{$self->{'_COLUMN_NAMES'}}; | 
|  | 233 |  |  |  |  | 512 |  | 
| 1047 | 233 |  |  |  |  | 358 | [ map { my %h; @h{@cn} = @{$_}; \%h } @{$self->getline_all (@args)} ]; | 
|  | 338 |  |  |  |  | 3755 |  | 
|  | 338 |  |  |  |  | 433 |  | 
|  | 338 |  |  |  |  | 1176 |  | 
|  | 338 |  |  |  |  | 1716 |  | 
|  | 233 |  |  |  |  | 6456 |  | 
| 1048 |  |  |  |  |  |  | } # getline_hr_all | 
| 1049 |  |  |  |  |  |  |  | 
| 1050 |  |  |  |  |  |  | sub say { | 
| 1051 | 13 |  |  | 13 | 1 | 2922 | my ($self, $io, @f) = @_; | 
| 1052 | 13 |  |  |  |  | 35 | my $eol = $self->eol (); | 
| 1053 | 13 | 100 | 33 |  |  | 93 | $eol eq "" and $self->eol ($\ || $/); | 
| 1054 |  |  |  |  |  |  | # say ($fh, undef) does not propage actual undef to print () | 
| 1055 | 13 | 100 | 66 |  |  | 200 | my $state = $self->print ($io, @f == 1 && !defined $f[0] ? undef : @f); | 
| 1056 | 13 |  |  |  |  | 148 | $self->eol ($eol); | 
| 1057 | 13 |  |  |  |  | 68 | return $state; | 
| 1058 |  |  |  |  |  |  | } # say | 
| 1059 |  |  |  |  |  |  |  | 
| 1060 |  |  |  |  |  |  | sub print_hr { | 
| 1061 | 3 |  |  | 3 | 1 | 308 | my ($self, $io, $hr) = @_; | 
| 1062 | 3 | 100 |  |  |  | 169 | $self->{'_COLUMN_NAMES'} or croak ($self->SetDiag (3009)); | 
| 1063 | 2 | 100 |  |  |  | 100 | ref $hr eq "HASH"      or croak ($self->SetDiag (3010)); | 
| 1064 | 1 |  |  |  |  | 4 | $self->print ($io, [ map { $hr->{$_} } $self->column_names () ]); | 
|  | 3 |  |  |  |  | 15 |  | 
| 1065 |  |  |  |  |  |  | } # print_hr | 
| 1066 |  |  |  |  |  |  |  | 
| 1067 |  |  |  |  |  |  | sub fragment { | 
| 1068 | 58 |  |  | 58 | 1 | 29070 | my ($self, $io, $spec) = @_; | 
| 1069 |  |  |  |  |  |  |  | 
| 1070 | 58 |  |  |  |  | 211 | my $qd = qr{\s* [0-9]+ \s* }x;		# digit | 
| 1071 | 58 |  |  |  |  | 136 | my $qs = qr{\s* (?: [0-9]+ | \* ) \s*}x;	# digit or star | 
| 1072 | 58 |  |  |  |  | 381 | my $qr = qr{$qd (?: - $qs )?}x;		# range | 
| 1073 | 58 |  |  |  |  | 304 | my $qc = qr{$qr (?: ; $qr )*}x;		# list | 
| 1074 | 58 | 100 | 100 |  |  | 3416 | defined $spec && $spec =~ m{^ \s* | 
| 1075 |  |  |  |  |  |  | \x23 ? \s*				# optional leading # | 
| 1076 |  |  |  |  |  |  | ( row | col | cell ) \s* = | 
| 1077 |  |  |  |  |  |  | ( $qc					# for row and col | 
| 1078 |  |  |  |  |  |  | | $qd , $qd (?: - $qs , $qs)?		# for cell (ranges) | 
| 1079 |  |  |  |  |  |  | (?: ; $qd , $qd (?: - $qs , $qs)? )*	# and cell (range) lists | 
| 1080 |  |  |  |  |  |  | ) \s* $}xi or croak ($self->SetDiag (2013)); | 
| 1081 | 38 |  |  |  |  | 179 | my ($type, $range) = (lc $1, $2); | 
| 1082 |  |  |  |  |  |  |  | 
| 1083 | 38 |  |  |  |  | 102 | my @h = $self->column_names (); | 
| 1084 |  |  |  |  |  |  |  | 
| 1085 | 38 |  |  |  |  | 69 | my @c; | 
| 1086 | 38 | 100 |  |  |  | 103 | if ($type eq "cell") { | 
| 1087 | 21 |  |  |  |  | 33 | my @spec; | 
| 1088 |  |  |  |  |  |  | my $min_row; | 
| 1089 | 21 |  |  |  |  | 29 | my $max_row = 0; | 
| 1090 | 21 |  |  |  |  | 104 | for (split m/\s*;\s*/ => $range) { | 
| 1091 | 37 | 100 |  |  |  | 315 | my ($tlr, $tlc, $brr, $brc) = (m{ | 
| 1092 |  |  |  |  |  |  | ^ \s* ([0-9]+     ) \s* , \s* ([0-9]+     ) \s* | 
| 1093 |  |  |  |  |  |  | (?: - \s* ([0-9]+ | \*) \s* , \s* ([0-9]+ | \*) \s* )? | 
| 1094 |  |  |  |  |  |  | $}x) or croak ($self->SetDiag (2013)); | 
| 1095 | 36 | 100 |  |  |  | 95 | defined $brr or ($brr, $brc) = ($tlr, $tlc); | 
| 1096 | 36 | 100 | 100 |  |  | 1091 | $tlr == 0 || $tlc == 0 || | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 1097 |  |  |  |  |  |  | ($brr ne "*" && ($brr == 0 || $brr < $tlr)) || | 
| 1098 |  |  |  |  |  |  | ($brc ne "*" && ($brc == 0 || $brc < $tlc)) | 
| 1099 |  |  |  |  |  |  | and croak ($self->SetDiag (2013)); | 
| 1100 | 28 |  |  |  |  | 40 | $tlc--; | 
| 1101 | 28 | 100 |  |  |  | 55 | $brc-- unless $brc eq "*"; | 
| 1102 | 28 | 100 |  |  |  | 52 | defined $min_row or $min_row = $tlr; | 
| 1103 | 28 | 100 |  |  |  | 52 | $tlr < $min_row and $min_row = $tlr; | 
| 1104 | 28 | 100 | 100 |  |  | 92 | $brr eq "*" || $brr > $max_row and | 
| 1105 |  |  |  |  |  |  | $max_row = $brr; | 
| 1106 | 28 |  |  |  |  | 98 | push @spec, [ $tlr, $tlc, $brr, $brc ]; | 
| 1107 |  |  |  |  |  |  | } | 
| 1108 | 12 |  |  |  |  | 18 | my $r = 0; | 
| 1109 | 12 |  |  |  |  | 377 | while (my $row = $self->getline ($io)) { | 
| 1110 | 77 | 100 |  |  |  | 2883 | ++$r < $min_row and next; | 
| 1111 | 33 |  |  |  |  | 53 | my %row; | 
| 1112 |  |  |  |  |  |  | my $lc; | 
| 1113 | 33 |  |  |  |  | 50 | foreach my $s (@spec) { | 
| 1114 | 77 |  |  |  |  | 103 | my ($tlr, $tlc, $brr, $brc) = @{$s}; | 
|  | 77 |  |  |  |  | 139 |  | 
| 1115 | 77 | 100 | 100 |  |  | 280 | $r <  $tlr || ($brr ne "*" && $r > $brr) and next; | 
|  |  |  | 100 |  |  |  |  | 
| 1116 | 45 | 100 | 100 |  |  | 110 | !defined $lc || $tlc < $lc and $lc = $tlc; | 
| 1117 | 45 | 100 |  |  |  | 78 | my $rr = $brc eq "*" ? $#{$row} : $brc; | 
|  | 5 |  |  |  |  | 10 |  | 
| 1118 | 45 |  |  |  |  | 231 | $row{$_} = $row->[$_] for $tlc .. $rr; | 
| 1119 |  |  |  |  |  |  | } | 
| 1120 | 33 |  |  |  |  | 115 | push @c, [ @row{sort { $a <=> $b } keys %row } ]; | 
|  | 59 |  |  |  |  | 161 |  | 
| 1121 | 33 | 100 |  |  |  | 89 | if (@h) { | 
| 1122 | 2 |  |  |  |  | 3 | my %h; @h{@h} = @{$c[-1]}; | 
|  | 2 |  |  |  |  | 4 |  | 
|  | 2 |  |  |  |  | 7 |  | 
| 1123 | 2 |  |  |  |  | 5 | $c[-1] = \%h; | 
| 1124 |  |  |  |  |  |  | } | 
| 1125 | 33 | 100 | 100 |  |  | 571 | $max_row ne "*" && $r == $max_row and last; | 
| 1126 |  |  |  |  |  |  | } | 
| 1127 | 12 |  |  |  |  | 140 | return \@c; | 
| 1128 |  |  |  |  |  |  | } | 
| 1129 |  |  |  |  |  |  |  | 
| 1130 |  |  |  |  |  |  | # row or col | 
| 1131 | 17 |  |  |  |  | 23 | my @r; | 
| 1132 | 17 |  |  |  |  | 24 | my $eod = 0; | 
| 1133 | 17 |  |  |  |  | 74 | for (split m/\s*;\s*/ => $range) { | 
| 1134 | 25 | 50 |  |  |  | 138 | my ($from, $to) = m/^\s* ([0-9]+) (?: \s* - \s* ([0-9]+ | \* ))? \s* $/x | 
| 1135 |  |  |  |  |  |  | or croak ($self->SetDiag (2013)); | 
| 1136 | 25 |  | 100 |  |  | 105 | $to ||= $from; | 
| 1137 | 25 | 100 |  |  |  | 50 | $to eq "*" and ($to, $eod) = ($from, 1); | 
| 1138 |  |  |  |  |  |  | # $to cannot be <= 0 due to regex and ||= | 
| 1139 | 25 | 100 | 100 |  |  | 403 | $from <= 0 || $to < $from and croak ($self->SetDiag (2013)); | 
| 1140 | 22 |  |  |  |  | 85 | $r[$_] = 1 for $from .. $to; | 
| 1141 |  |  |  |  |  |  | } | 
| 1142 |  |  |  |  |  |  |  | 
| 1143 | 14 |  |  |  |  | 33 | my $r = 0; | 
| 1144 | 14 | 100 |  |  |  | 35 | $type eq "col" and shift @r; | 
| 1145 | 14 |  | 100 |  |  | 146 | $_ ||= 0 for @r; | 
| 1146 | 14 |  |  |  |  | 505 | while (my $row = $self->getline ($io)) { | 
| 1147 | 109 |  |  |  |  | 3209 | $r++; | 
| 1148 | 109 | 100 |  |  |  | 207 | if ($type eq "row") { | 
| 1149 | 64 | 100 | 100 |  |  | 267 | if (($r > $#r && $eod) || $r[$r]) { | 
|  |  |  | 100 |  |  |  |  | 
| 1150 | 20 |  |  |  |  | 34 | push @c, $row; | 
| 1151 | 20 | 100 |  |  |  | 43 | if (@h) { | 
| 1152 | 3 |  |  |  |  | 6 | my %h; @h{@h} = @{$c[-1]}; | 
|  | 3 |  |  |  |  | 5 |  | 
|  | 3 |  |  |  |  | 14 |  | 
| 1153 | 3 |  |  |  |  | 9 | $c[-1] = \%h; | 
| 1154 |  |  |  |  |  |  | } | 
| 1155 |  |  |  |  |  |  | } | 
| 1156 | 64 |  |  |  |  | 1247 | next; | 
| 1157 |  |  |  |  |  |  | } | 
| 1158 | 45 | 100 | 100 |  |  | 69 | push @c, [ map { ($_ > $#r && $eod) || $r[$_] ? $row->[$_] : () } 0..$#{$row} ]; | 
|  | 405 |  |  |  |  | 1413 |  | 
|  | 45 |  |  |  |  | 80 |  | 
| 1159 | 45 | 100 |  |  |  | 777 | if (@h) { | 
| 1160 | 9 |  |  |  |  | 13 | my %h; @h{@h} = @{$c[-1]}; | 
|  | 9 |  |  |  |  | 11 |  | 
|  | 9 |  |  |  |  | 87 |  | 
| 1161 | 9 |  |  |  |  | 193 | $c[-1] = \%h; | 
| 1162 |  |  |  |  |  |  | } | 
| 1163 |  |  |  |  |  |  | } | 
| 1164 |  |  |  |  |  |  |  | 
| 1165 | 14 |  |  |  |  | 356 | return \@c; | 
| 1166 |  |  |  |  |  |  | } # fragment | 
| 1167 |  |  |  |  |  |  |  | 
| 1168 |  |  |  |  |  |  | my $csv_usage = q{usage: my $aoa = csv (in => $file);}; | 
| 1169 |  |  |  |  |  |  |  | 
| 1170 |  |  |  |  |  |  | sub _csv_attr { | 
| 1171 | 300 | 100 | 66 | 300 |  | 1618 | my %attr = (@_ == 1 && ref $_[0] eq "HASH" ? %{$_[0]} : @_) or croak (); | 
|  | 4 | 50 |  |  |  | 23 |  | 
| 1172 |  |  |  |  |  |  |  | 
| 1173 | 300 |  |  |  |  | 606 | $attr{'binary'} = 1; | 
| 1174 |  |  |  |  |  |  |  | 
| 1175 | 300 |  | 100 |  |  | 1372 | my $enc = delete $attr{'enc'} || delete $attr{'encoding'} || ""; | 
| 1176 | 300 | 100 |  |  |  | 671 | $enc eq "auto" and ($attr{'detect_bom'}, $enc) = (1, ""); | 
| 1177 | 300 | 50 |  |  |  | 723 | my $stack = $enc =~ s/(:\w.*)// ? $1 : ""; | 
| 1178 | 300 | 100 |  |  |  | 613 | $enc =~ m/^[-\w.]+$/ and $enc = ":encoding($enc)"; | 
| 1179 | 300 |  |  |  |  | 557 | $enc .= $stack; | 
| 1180 |  |  |  |  |  |  |  | 
| 1181 | 300 |  |  |  |  | 390 | my $fh; | 
| 1182 | 300 |  |  |  |  | 418 | my $sink = 0; | 
| 1183 | 300 |  |  |  |  | 416 | my $cls  = 0;	# If I open a file, I have to close it | 
| 1184 | 300 | 100 | 100 |  |  | 1351 | my $in   = delete $attr{'in'}  || delete $attr{'file'} or croak ($csv_usage); | 
| 1185 |  |  |  |  |  |  | my $out  = exists $attr{'out'} && !$attr{'out'} ? \"skip" | 
| 1186 | 297 | 50 | 66 |  |  | 1256 | : delete $attr{'out'} || delete $attr{'file'}; | 
|  |  |  | 100 |  |  |  |  | 
| 1187 |  |  |  |  |  |  |  | 
| 1188 | 297 | 100 | 100 |  |  | 1010 | ref $in eq "CODE" || ref $in eq "ARRAY" and $out ||= \*STDOUT; | 
|  |  |  | 100 |  |  |  |  | 
| 1189 |  |  |  |  |  |  |  | 
| 1190 | 297 | 100 | 66 |  |  | 1163 | $in && $out && !ref $in && !ref $out and croak (join "\n" => | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 1191 |  |  |  |  |  |  | qq{Cannot use a string for both in and out. Instead use:}, | 
| 1192 |  |  |  |  |  |  | qq{ csv (in => csv (in => "$in"), out => "$out");\n}); | 
| 1193 |  |  |  |  |  |  |  | 
| 1194 | 296 | 100 |  |  |  | 545 | if ($out) { | 
| 1195 | 32 | 100 | 100 |  |  | 244 | if (ref $out and ("ARRAY" eq ref $out or "HASH" eq ref $out)) { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 1196 | 5 |  |  |  |  | 10 | delete $attr{'out'}; | 
| 1197 | 5 |  |  |  |  | 6 | $sink = 1; | 
| 1198 |  |  |  |  |  |  | } | 
| 1199 |  |  |  |  |  |  | elsif ((ref $out and "SCALAR" ne ref $out) or "GLOB" eq ref \$out) { | 
| 1200 | 14 |  |  |  |  | 22 | $fh = $out; | 
| 1201 |  |  |  |  |  |  | } | 
| 1202 | 6 |  |  |  |  | 21 | elsif (ref $out and "SCALAR" eq ref $out and defined ${$out} and ${$out} eq "skip") { | 
|  | 6 |  |  |  |  | 18 |  | 
| 1203 | 1 |  |  |  |  | 3 | delete $attr{'out'}; | 
| 1204 | 1 |  |  |  |  | 2 | $sink = 1; | 
| 1205 |  |  |  |  |  |  | } | 
| 1206 |  |  |  |  |  |  | else { | 
| 1207 | 12 | 100 |  |  |  | 623 | open $fh, ">", $out or croak ("$out: $!"); | 
| 1208 | 11 |  |  |  |  | 36 | $cls = 1; | 
| 1209 |  |  |  |  |  |  | } | 
| 1210 | 31 | 100 |  |  |  | 72 | if ($fh) { | 
| 1211 | 25 | 100 |  |  |  | 43 | if ($enc) { | 
| 1212 | 1 |  |  |  |  | 8 | binmode $fh, $enc; | 
| 1213 | 1 |  |  |  |  | 55 | my $fn = fileno $fh; # This is a workaround for a bug in PerlIO::via::gzip | 
| 1214 |  |  |  |  |  |  | } | 
| 1215 | 25 | 100 |  |  |  | 63 | unless (defined $attr{'eol'}) { | 
| 1216 | 18 |  |  |  |  | 47 | my @layers = eval { PerlIO::get_layers ($fh) }; | 
|  | 18 |  |  |  |  | 106 |  | 
| 1217 | 18 | 100 |  |  |  | 112 | $attr{'eol'} = (grep m/crlf/ => @layers) ? "\n" : "\r\n"; | 
| 1218 |  |  |  |  |  |  | } | 
| 1219 |  |  |  |  |  |  | } | 
| 1220 |  |  |  |  |  |  | } | 
| 1221 |  |  |  |  |  |  |  | 
| 1222 | 295 | 100 | 100 |  |  | 1602 | if (   ref $in eq "CODE" or ref $in eq "ARRAY") { | 
|  |  | 100 | 100 |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
| 1223 |  |  |  |  |  |  | # All done | 
| 1224 |  |  |  |  |  |  | } | 
| 1225 |  |  |  |  |  |  | elsif (ref $in eq "SCALAR") { | 
| 1226 |  |  |  |  |  |  | # Strings with code points over 0xFF may not be mapped into in-memory file handles | 
| 1227 |  |  |  |  |  |  | # "<$enc" does not change that :( | 
| 1228 | 23 | 50 |  | 5 |  | 315 | open $fh, "<", $in or croak ("Cannot open from SCALAR using PerlIO"); | 
|  | 5 |  |  |  |  | 46 |  | 
|  | 5 |  |  |  |  | 13 |  | 
|  | 5 |  |  |  |  | 38 |  | 
| 1229 | 23 |  |  |  |  | 1853 | $cls = 1; | 
| 1230 |  |  |  |  |  |  | } | 
| 1231 |  |  |  |  |  |  | elsif (ref $in or "GLOB" eq ref \$in) { | 
| 1232 | 16 | 50 | 66 |  |  | 47 | if (!ref $in && $] < 5.008005) { | 
| 1233 | 0 |  |  |  |  | 0 | $fh = \*{$in}; # uncoverable statement ancient perl version required | 
|  | 0 |  |  |  |  | 0 |  | 
| 1234 |  |  |  |  |  |  | } | 
| 1235 |  |  |  |  |  |  | else { | 
| 1236 | 16 |  |  |  |  | 28 | $fh = $in; | 
| 1237 |  |  |  |  |  |  | } | 
| 1238 |  |  |  |  |  |  | } | 
| 1239 |  |  |  |  |  |  | else { | 
| 1240 | 232 | 100 |  |  |  | 8988 | open $fh, "<$enc", $in or croak ("$in: $!"); | 
| 1241 | 230 |  |  |  |  | 17629 | $cls = 1; | 
| 1242 |  |  |  |  |  |  | } | 
| 1243 | 293 | 50 | 33 |  |  | 780 | $fh || $sink or croak (qq{No valid source passed. "in" is required}); | 
| 1244 |  |  |  |  |  |  |  | 
| 1245 | 293 |  |  |  |  | 538 | my $hdrs = delete $attr{'headers'}; | 
| 1246 | 293 |  |  |  |  | 467 | my $frag = delete $attr{'fragment'}; | 
| 1247 | 293 |  |  |  |  | 471 | my $key  = delete $attr{'key'}; | 
| 1248 | 293 |  |  |  |  | 428 | my $val  = delete $attr{'value'}; | 
| 1249 |  |  |  |  |  |  | my $kh   = delete $attr{'keep_headers'}		|| | 
| 1250 |  |  |  |  |  |  | delete $attr{'keep_column_names'}	|| | 
| 1251 | 293 |  | 100 |  |  | 1283 | delete $attr{'kh'}; | 
| 1252 |  |  |  |  |  |  |  | 
| 1253 |  |  |  |  |  |  | my $cbai = delete $attr{'callbacks'}{'after_in'}	|| | 
| 1254 |  |  |  |  |  |  | delete $attr{'after_in'}			|| | 
| 1255 |  |  |  |  |  |  | delete $attr{'callbacks'}{'after_parse'}	|| | 
| 1256 | 293 |  | 100 |  |  | 1784 | delete $attr{'after_parse'}; | 
| 1257 |  |  |  |  |  |  | my $cbbo = delete $attr{'callbacks'}{'before_out'}	|| | 
| 1258 | 293 |  | 100 |  |  | 861 | delete $attr{'before_out'}; | 
| 1259 |  |  |  |  |  |  | my $cboi = delete $attr{'callbacks'}{'on_in'}	|| | 
| 1260 | 293 |  | 100 |  |  | 756 | delete $attr{'on_in'}; | 
| 1261 |  |  |  |  |  |  |  | 
| 1262 |  |  |  |  |  |  | my $hd_s = delete $attr{'sep_set'}			|| | 
| 1263 | 293 |  | 100 |  |  | 742 | delete $attr{'seps'}; | 
| 1264 |  |  |  |  |  |  | my $hd_b = delete $attr{'detect_bom'}		|| | 
| 1265 | 293 |  | 100 |  |  | 847 | delete $attr{'bom'}; | 
| 1266 |  |  |  |  |  |  | my $hd_m = delete $attr{'munge'}			|| | 
| 1267 | 293 |  | 100 |  |  | 796 | delete $attr{'munge_column_names'}; | 
| 1268 | 293 |  |  |  |  | 422 | my $hd_c = delete $attr{'set_column_names'}; | 
| 1269 |  |  |  |  |  |  |  | 
| 1270 | 293 |  |  |  |  | 1037 | for ([ 'quo'    => "quote"		], | 
| 1271 |  |  |  |  |  |  | [ 'esc'    => "escape"		], | 
| 1272 |  |  |  |  |  |  | [ 'escape' => "escape_char"	], | 
| 1273 |  |  |  |  |  |  | ) { | 
| 1274 | 879 |  |  |  |  | 1146 | my ($f, $t) = @{$_}; | 
|  | 879 |  |  |  |  | 1550 |  | 
| 1275 | 879 | 100 | 100 |  |  | 2118 | exists $attr{$f} and !exists $attr{$t} and $attr{$t} = delete $attr{$f}; | 
| 1276 |  |  |  |  |  |  | } | 
| 1277 |  |  |  |  |  |  |  | 
| 1278 | 293 |  |  |  |  | 687 | my $fltr = delete $attr{'filter'}; | 
| 1279 |  |  |  |  |  |  | my %fltr = ( | 
| 1280 | 10 | 100 | 33 | 10 |  | 13 | 'not_blank' => sub { @{$_[1]} > 1 or defined $_[1][0] && $_[1][0] ne "" }, | 
|  | 10 |  |  |  |  | 75 |  | 
| 1281 | 10 | 50 |  | 10 |  | 12 | 'not_empty' => sub { grep { defined && $_ ne "" } @{$_[1]} }, | 
|  | 26 |  |  |  |  | 178 |  | 
|  | 10 |  |  |  |  | 18 |  | 
| 1282 | 10 | 50 |  | 10 |  | 13 | 'filled'    => sub { grep { defined && m/\S/    } @{$_[1]} }, | 
|  | 26 |  |  |  |  | 244 |  | 
|  | 10 |  |  |  |  | 20 |  | 
| 1283 | 293 |  |  |  |  | 2008 | ); | 
| 1284 |  |  |  |  |  |  | defined $fltr && !ref $fltr && exists $fltr{$fltr} and | 
| 1285 | 293 | 50 | 100 |  |  | 814 | $fltr = { '0' => $fltr{$fltr} }; | 
|  |  |  | 66 |  |  |  |  | 
| 1286 | 293 | 100 |  |  |  | 670 | ref $fltr eq "CODE" and $fltr = { 0 => $fltr }; | 
| 1287 | 293 | 100 |  |  |  | 587 | ref $fltr eq "HASH" or  $fltr = undef; | 
| 1288 |  |  |  |  |  |  |  | 
| 1289 | 293 |  |  |  |  | 451 | my $form = delete $attr{'formula'}; | 
| 1290 |  |  |  |  |  |  |  | 
| 1291 | 293 | 100 |  |  |  | 712 | defined $attr{'auto_diag'}   or $attr{'auto_diag'}   = 1; | 
| 1292 | 293 | 100 |  |  |  | 638 | defined $attr{'escape_null'} or $attr{'escape_null'} = 0; | 
| 1293 | 293 | 50 | 66 |  |  | 1429 | my $csv = delete $attr{'csv'} || Text::CSV_XS->new (\%attr) | 
| 1294 |  |  |  |  |  |  | or croak ($last_new_err); | 
| 1295 | 293 | 100 |  |  |  | 600 | defined $form and $csv->formula ($form); | 
| 1296 |  |  |  |  |  |  |  | 
| 1297 | 293 | 100 | 100 |  |  | 759 | $kh && !ref $kh && $kh =~ m/^(?:1|yes|true|internal|auto)$/i and | 
|  |  |  | 100 |  |  |  |  | 
| 1298 |  |  |  |  |  |  | $kh = \@internal_kh; | 
| 1299 |  |  |  |  |  |  |  | 
| 1300 |  |  |  |  |  |  | return { | 
| 1301 | 293 |  |  |  |  | 5126 | 'csv'  => $csv, | 
| 1302 |  |  |  |  |  |  | 'attr' => { %attr }, | 
| 1303 |  |  |  |  |  |  | 'fh'   => $fh, | 
| 1304 |  |  |  |  |  |  | 'cls'  => $cls, | 
| 1305 |  |  |  |  |  |  | 'in'   => $in, | 
| 1306 |  |  |  |  |  |  | 'sink' => $sink, | 
| 1307 |  |  |  |  |  |  | 'out'  => $out, | 
| 1308 |  |  |  |  |  |  | 'enc'  => $enc, | 
| 1309 |  |  |  |  |  |  | 'hdrs' => $hdrs, | 
| 1310 |  |  |  |  |  |  | 'key'  => $key, | 
| 1311 |  |  |  |  |  |  | 'val'  => $val, | 
| 1312 |  |  |  |  |  |  | 'kh'   => $kh, | 
| 1313 |  |  |  |  |  |  | 'frag' => $frag, | 
| 1314 |  |  |  |  |  |  | 'fltr' => $fltr, | 
| 1315 |  |  |  |  |  |  | 'cbai' => $cbai, | 
| 1316 |  |  |  |  |  |  | 'cbbo' => $cbbo, | 
| 1317 |  |  |  |  |  |  | 'cboi' => $cboi, | 
| 1318 |  |  |  |  |  |  | 'hd_s' => $hd_s, | 
| 1319 |  |  |  |  |  |  | 'hd_b' => $hd_b, | 
| 1320 |  |  |  |  |  |  | 'hd_m' => $hd_m, | 
| 1321 |  |  |  |  |  |  | 'hd_c' => $hd_c, | 
| 1322 |  |  |  |  |  |  | }; | 
| 1323 |  |  |  |  |  |  | } # _csv_attr | 
| 1324 |  |  |  |  |  |  |  | 
| 1325 |  |  |  |  |  |  | sub csv { | 
| 1326 | 301 | 100 | 100 | 301 | 1 | 67746 | @_ && ref $_[0] eq __PACKAGE__ and splice @_, 0, 0, "csv"; | 
| 1327 | 301 | 100 |  |  |  | 787 | @_ or croak ($csv_usage); | 
| 1328 |  |  |  |  |  |  |  | 
| 1329 | 300 |  |  |  |  | 726 | my $c = _csv_attr (@_); | 
| 1330 |  |  |  |  |  |  |  | 
| 1331 | 293 |  |  |  |  | 580 | my ($csv, $in, $fh, $hdrs) = @{$c}{qw( csv in fh hdrs )}; | 
|  | 293 |  |  |  |  | 833 |  | 
| 1332 | 293 |  |  |  |  | 420 | my %hdr; | 
| 1333 | 293 | 100 |  |  |  | 623 | if (ref $hdrs eq "HASH") { | 
| 1334 | 2 |  |  |  |  | 7 | %hdr  = %{$hdrs}; | 
|  | 2 |  |  |  |  | 7 |  | 
| 1335 | 2 |  |  |  |  | 5 | $hdrs = "auto"; | 
| 1336 |  |  |  |  |  |  | } | 
| 1337 |  |  |  |  |  |  |  | 
| 1338 | 293 | 100 | 100 |  |  | 688 | if ($c->{'out'} && !$c->{'sink'}) { | 
| 1339 |  |  |  |  |  |  | !$hdrs && ref $c->{'kh'} && $c->{'kh'} == \@internal_kh and | 
| 1340 | 24 | 100 | 100 |  |  | 86 | $hdrs = $c->{'kh'}; | 
|  |  |  | 66 |  |  |  |  | 
| 1341 |  |  |  |  |  |  |  | 
| 1342 | 24 | 100 | 100 |  |  | 54 | if (ref $in eq "CODE") { | 
|  |  | 100 |  |  |  |  |  | 
| 1343 | 3 |  |  |  |  | 7 | my $hdr = 1; | 
| 1344 | 3 |  |  |  |  | 11 | while (my $row = $in->($csv)) { | 
| 1345 | 7 | 100 |  |  |  | 52 | if (ref $row eq "ARRAY") { | 
| 1346 | 3 |  |  |  |  | 29 | $csv->print ($fh, $row); | 
| 1347 | 3 |  |  |  |  | 26 | next; | 
| 1348 |  |  |  |  |  |  | } | 
| 1349 | 4 | 50 |  |  |  | 23 | if (ref $row eq "HASH") { | 
| 1350 | 4 | 100 |  |  |  | 12 | if ($hdr) { | 
| 1351 | 2 | 50 | 100 |  |  | 6 | $hdrs ||= [ map { $hdr{$_} || $_ } keys %{$row} ]; | 
|  | 3 |  |  |  |  | 13 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 1352 | 2 |  |  |  |  | 37 | $csv->print ($fh, $hdrs); | 
| 1353 | 2 |  |  |  |  | 38 | $hdr = 0; | 
| 1354 |  |  |  |  |  |  | } | 
| 1355 | 4 |  |  |  |  | 6 | $csv->print ($fh, [ @{$row}{@{$hdrs}} ]); | 
|  | 4 |  |  |  |  | 24 |  | 
|  | 4 |  |  |  |  | 8 |  | 
| 1356 |  |  |  |  |  |  | } | 
| 1357 |  |  |  |  |  |  | } | 
| 1358 |  |  |  |  |  |  | } | 
| 1359 | 21 |  |  |  |  | 84 | elsif (@{$in} == 0 or ref $in->[0] eq "ARRAY") { # aoa | 
| 1360 | 10 | 50 |  |  |  | 22 | ref $hdrs and $csv->print ($fh, $hdrs); | 
| 1361 | 10 |  |  |  |  | 14 | for (@{$in}) { | 
|  | 10 |  |  |  |  | 21 |  | 
| 1362 | 12 | 100 |  |  |  | 68 | $c->{'cboi'} and $c->{'cboi'}->($csv, $_); | 
| 1363 | 12 | 50 |  |  |  | 1086 | $c->{'cbbo'} and $c->{'cbbo'}->($csv, $_); | 
| 1364 | 12 |  |  |  |  | 191 | $csv->print ($fh, $_); | 
| 1365 |  |  |  |  |  |  | } | 
| 1366 |  |  |  |  |  |  | } | 
| 1367 |  |  |  |  |  |  | else { # aoh | 
| 1368 | 11 | 100 |  |  |  | 26 | my @hdrs = ref $hdrs ? @{$hdrs} : keys %{$in->[0]}; | 
|  | 5 |  |  |  |  | 12 |  | 
|  | 6 |  |  |  |  | 19 |  | 
| 1369 | 11 | 100 |  |  |  | 27 | defined $hdrs or $hdrs = "auto"; | 
| 1370 |  |  |  |  |  |  | ref $hdrs || $hdrs eq "auto" and @hdrs and | 
| 1371 | 11 | 100 | 100 |  |  | 50 | $csv->print ($fh, [ map { $hdr{$_} || $_ } @hdrs ]); | 
|  | 20 | 100 | 66 |  |  | 250 |  | 
| 1372 | 11 |  |  |  |  | 119 | for (@{$in}) { | 
|  | 11 |  |  |  |  | 28 |  | 
| 1373 | 17 |  |  |  |  | 82 | local %_; | 
| 1374 | 17 |  |  |  |  | 36 | *_ = $_; | 
| 1375 | 17 | 50 |  |  |  | 39 | $c->{'cboi'} and $c->{'cboi'}->($csv, $_); | 
| 1376 | 17 | 50 |  |  |  | 33 | $c->{'cbbo'} and $c->{'cbbo'}->($csv, $_); | 
| 1377 | 17 |  |  |  |  | 29 | $csv->print ($fh, [ @{$_}{@hdrs} ]); | 
|  | 17 |  |  |  |  | 107 |  | 
| 1378 |  |  |  |  |  |  | } | 
| 1379 |  |  |  |  |  |  | } | 
| 1380 |  |  |  |  |  |  |  | 
| 1381 | 24 | 100 |  |  |  | 771 | $c->{'cls'} and close $fh; | 
| 1382 | 24 |  |  |  |  | 307 | return 1; | 
| 1383 |  |  |  |  |  |  | } | 
| 1384 |  |  |  |  |  |  |  | 
| 1385 | 269 |  |  |  |  | 367 | my @row1; | 
| 1386 | 269 | 100 | 100 |  |  | 1398 | if (defined $c->{'hd_s'} || defined $c->{'hd_b'} || defined $c->{'hd_m'} || defined $c->{'hd_c'}) { | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 100 |  |  |  |  | 
| 1387 | 163 |  |  |  |  | 236 | my %harg; | 
| 1388 |  |  |  |  |  |  | !defined $c->{'hd_s'} &&  $c->{'attr'}{'sep_char'} and | 
| 1389 | 163 | 100 | 100 |  |  | 570 | $c->{'hd_s'} = [ $c->{'attr'}{'sep_char'} ]; | 
| 1390 |  |  |  |  |  |  | !defined $c->{'hd_s'} &&  $c->{'attr'}{'sep'} and | 
| 1391 | 163 | 100 | 100 |  |  | 585 | $c->{'hd_s'} = [ $c->{'attr'}{'sep'} ]; | 
| 1392 | 163 | 100 |  |  |  | 303 | defined  $c->{'hd_s'} and $harg{'sep_set'}            = $c->{'hd_s'}; | 
| 1393 | 163 | 50 |  |  |  | 315 | defined  $c->{'hd_d'} and $harg{'detect_bom'}         = $c->{'hd_b'}; | 
| 1394 | 163 | 50 |  |  |  | 303 | defined  $c->{'hd_m'} and $harg{'munge_column_names'} = $hdrs ? "none" : $c->{'hd_m'}; | 
|  |  | 100 |  |  |  |  |  | 
| 1395 | 163 | 50 |  |  |  | 311 | defined  $c->{'hd_c'} and $harg{'set_column_names'}   = $hdrs ? 0      : $c->{'hd_c'}; | 
|  |  | 100 |  |  |  |  |  | 
| 1396 | 163 |  |  |  |  | 393 | @row1 = $csv->header ($fh, \%harg); | 
| 1397 | 160 |  |  |  |  | 396 | my @hdr = $csv->column_names (); | 
| 1398 | 160 | 100 | 50 |  |  | 791 | @hdr and $hdrs ||= \@hdr; | 
| 1399 |  |  |  |  |  |  | } | 
| 1400 |  |  |  |  |  |  |  | 
| 1401 | 266 | 100 |  |  |  | 668 | if ($c->{'kh'}) { | 
| 1402 | 15 |  |  |  |  | 28 | @internal_kh = (); | 
| 1403 | 15 | 100 |  |  |  | 711 | ref $c->{'kh'} eq "ARRAY" or croak ($csv->SetDiag (1501)); | 
| 1404 | 10 |  | 100 |  |  | 27 | $hdrs ||= "auto"; | 
| 1405 |  |  |  |  |  |  | } | 
| 1406 |  |  |  |  |  |  |  | 
| 1407 | 261 |  |  |  |  | 453 | my $key = $c->{'key'}; | 
| 1408 | 261 | 100 |  |  |  | 486 | if ($key) { | 
| 1409 | 27 | 100 | 100 |  |  | 550 | !ref $key or ref $key eq "ARRAY" && @{$key} > 1 or croak ($csv->SetDiag (1501)); | 
|  | 8 |  | 100 |  |  | 436 |  | 
| 1410 | 20 |  | 100 |  |  | 56 | $hdrs ||= "auto"; | 
| 1411 |  |  |  |  |  |  | } | 
| 1412 | 254 |  |  |  |  | 446 | my $val = $c->{'val'}; | 
| 1413 | 254 | 100 |  |  |  | 479 | if ($val) { | 
| 1414 | 9 | 100 |  |  |  | 134 | $key						or croak ($csv->SetDiag (1502)); | 
| 1415 | 8 | 100 | 100 |  |  | 263 | !ref $val or ref $val eq "ARRAY" && @{$val} > 0 or croak ($csv->SetDiag (1503)); | 
|  | 3 |  | 100 |  |  | 118 |  | 
| 1416 |  |  |  |  |  |  | } | 
| 1417 |  |  |  |  |  |  |  | 
| 1418 | 250 | 100 | 100 |  |  | 519 | $c->{'fltr'} && grep m/\D/ => keys %{$c->{'fltr'}} and $hdrs ||= "auto"; | 
|  | 16 |  | 100 |  |  | 107 |  | 
| 1419 | 250 | 100 |  |  |  | 470 | if (defined $hdrs) { | 
| 1420 | 208 | 100 |  |  |  | 584 | if (!ref $hdrs) { | 
|  |  | 100 |  |  |  |  |  | 
| 1421 | 45 | 100 |  |  |  | 108 | if ($hdrs eq "skip") { | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 100 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 1422 | 1 |  |  |  |  | 40 | $csv->getline ($fh); # discard; | 
| 1423 |  |  |  |  |  |  | } | 
| 1424 |  |  |  |  |  |  | elsif ($hdrs eq "auto") { | 
| 1425 | 42 | 50 |  |  |  | 1624 | my $h = $csv->getline ($fh) or return; | 
| 1426 | 42 | 100 |  |  |  | 2138 | $hdrs = [ map {      $hdr{$_} || $_ } @{$h} ]; | 
|  | 122 |  |  |  |  | 502 |  | 
|  | 42 |  |  |  |  | 91 |  | 
| 1427 |  |  |  |  |  |  | } | 
| 1428 |  |  |  |  |  |  | elsif ($hdrs eq "lc") { | 
| 1429 | 1 | 50 |  |  |  | 39 | my $h = $csv->getline ($fh) or return; | 
| 1430 | 1 |  | 33 |  |  | 57 | $hdrs = [ map { lc ($hdr{$_} || $_) } @{$h} ]; | 
|  | 3 |  |  |  |  | 14 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 1431 |  |  |  |  |  |  | } | 
| 1432 |  |  |  |  |  |  | elsif ($hdrs eq "uc") { | 
| 1433 | 1 | 50 |  |  |  | 39 | my $h = $csv->getline ($fh) or return; | 
| 1434 | 1 |  | 33 |  |  | 61 | $hdrs = [ map { uc ($hdr{$_} || $_) } @{$h} ]; | 
|  | 3 |  |  |  |  | 14 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 1435 |  |  |  |  |  |  | } | 
| 1436 |  |  |  |  |  |  | } | 
| 1437 |  |  |  |  |  |  | elsif (ref $hdrs eq "CODE") { | 
| 1438 | 1 | 50 |  |  |  | 40 | my $h  = $csv->getline ($fh) or return; | 
| 1439 | 1 |  |  |  |  | 47 | my $cr = $hdrs; | 
| 1440 | 1 |  | 33 |  |  | 2 | $hdrs  = [ map {  $cr->($hdr{$_} || $_) } @{$h} ]; | 
|  | 3 |  |  |  |  | 20 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 1441 |  |  |  |  |  |  | } | 
| 1442 | 208 | 100 | 66 |  |  | 580 | $c->{'kh'} and $hdrs and @{$c->{'kh'}} = @{$hdrs}; | 
|  | 10 |  |  |  |  | 28 |  | 
|  | 10 |  |  |  |  | 19 |  | 
| 1443 |  |  |  |  |  |  | } | 
| 1444 |  |  |  |  |  |  |  | 
| 1445 | 250 | 100 |  |  |  | 457 | if ($c->{'fltr'}) { | 
| 1446 | 16 |  |  |  |  | 28 | my %f = %{$c->{'fltr'}}; | 
|  | 16 |  |  |  |  | 48 |  | 
| 1447 |  |  |  |  |  |  | # convert headers to index | 
| 1448 | 16 |  |  |  |  | 27 | my @hdr; | 
| 1449 | 16 | 100 |  |  |  | 35 | if (ref $hdrs) { | 
| 1450 | 7 |  |  |  |  | 10 | @hdr = @{$hdrs}; | 
|  | 7 |  |  |  |  | 20 |  | 
| 1451 | 7 |  |  |  |  | 21 | for (0 .. $#hdr) { | 
| 1452 | 21 | 100 |  |  |  | 60 | exists $f{$hdr[$_]} and $f{$_ + 1} = delete $f{$hdr[$_]}; | 
| 1453 |  |  |  |  |  |  | } | 
| 1454 |  |  |  |  |  |  | } | 
| 1455 |  |  |  |  |  |  | $csv->callbacks ('after_parse' => sub { | 
| 1456 | 114 |  |  | 114 |  | 4020 | my ($CSV, $ROW) = @_; # lexical sub-variables in caps | 
| 1457 | 114 |  |  |  |  | 368 | foreach my $FLD (sort keys %f) { | 
| 1458 | 115 |  |  |  |  | 284 | local $_ = $ROW->[$FLD - 1]; | 
| 1459 | 115 |  |  |  |  | 161 | local %_; | 
| 1460 | 115 | 100 |  |  |  | 213 | @hdr and @_{@hdr} = @{$ROW}; | 
|  | 51 |  |  |  |  | 150 |  | 
| 1461 | 115 | 100 |  |  |  | 268 | $f{$FLD}->($CSV, $ROW) or return \"skip"; | 
| 1462 | 52 |  |  |  |  | 1185 | $ROW->[$FLD - 1] = $_; | 
| 1463 |  |  |  |  |  |  | } | 
| 1464 | 16 |  |  |  |  | 85 | }); | 
| 1465 |  |  |  |  |  |  | } | 
| 1466 |  |  |  |  |  |  |  | 
| 1467 | 250 |  |  |  |  | 372 | my $frag = $c->{'frag'}; | 
| 1468 |  |  |  |  |  |  | my $ref = ref $hdrs | 
| 1469 |  |  |  |  |  |  | ? # aoh | 
| 1470 | 250 | 100 |  |  |  | 2452 | do { | 
|  |  | 100 |  |  |  |  |  | 
| 1471 | 207 |  |  |  |  | 421 | my @h = $csv->column_names ($hdrs); | 
| 1472 | 207 |  |  |  |  | 307 | my %h; $h{$_}++ for @h; | 
|  | 207 |  |  |  |  | 680 |  | 
| 1473 | 207 | 50 |  |  |  | 483 | exists $h{""} and croak ($csv->SetDiag (1012)); | 
| 1474 | 207 | 50 |  |  |  | 476 | unless (keys %h == @h) { | 
| 1475 |  |  |  |  |  |  | croak ($csv->_SetDiagInfo (1013, join ", " => | 
| 1476 | 0 |  |  |  |  | 0 | map { "$_ ($h{$_})" } grep { $h{$_} > 1 } keys %h)); | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 1477 |  |  |  |  |  |  | } | 
| 1478 |  |  |  |  |  |  | $frag ? $csv->fragment ($fh, $frag) : | 
| 1479 | 207 | 100 |  |  |  | 646 | $key  ? do { | 
|  |  | 100 |  |  |  |  |  | 
| 1480 | 17 | 100 |  |  |  | 53 | my ($k, $j, @f) = ref $key ? (undef, @{$key}) : ($key); | 
|  | 5 |  |  |  |  | 13 |  | 
| 1481 | 17 | 100 |  |  |  | 34 | if (my @mk = grep { !exists $h{$_} } grep { defined } $k, @f) { | 
|  | 22 |  |  |  |  | 84 |  | 
|  | 27 |  |  |  |  | 58 |  | 
| 1482 | 2 |  |  |  |  | 11 | croak ($csv->_SetDiagInfo (4001, join ", " => @mk)); | 
| 1483 |  |  |  |  |  |  | } | 
| 1484 |  |  |  |  |  |  | +{ map { | 
| 1485 | 26 |  |  |  |  | 40 | my $r = $_; | 
| 1486 | 26 | 100 |  |  |  | 66 | my $K = defined $k ? $r->{$k} : join $j => @{$r}{@f}; | 
|  | 4 |  |  |  |  | 12 |  | 
| 1487 |  |  |  |  |  |  | ( $K => ( | 
| 1488 |  |  |  |  |  |  | $val | 
| 1489 |  |  |  |  |  |  | ? ref $val | 
| 1490 | 4 |  |  |  |  | 22 | ? { map { $_ => $r->{$_} } @{$val} } | 
|  | 2 |  |  |  |  | 4 |  | 
| 1491 | 26 | 100 |  |  |  | 111 | : $r->{$val} | 
|  |  | 100 |  |  |  |  |  | 
| 1492 |  |  |  |  |  |  | : $r )); | 
| 1493 | 15 |  |  |  |  | 25 | } @{$csv->getline_hr_all ($fh)} } | 
|  | 15 |  |  |  |  | 36 |  | 
| 1494 |  |  |  |  |  |  | } | 
| 1495 |  |  |  |  |  |  | : $csv->getline_hr_all ($fh); | 
| 1496 |  |  |  |  |  |  | } | 
| 1497 |  |  |  |  |  |  | : # aoa | 
| 1498 |  |  |  |  |  |  | $frag ? $csv->fragment ($fh, $frag) | 
| 1499 |  |  |  |  |  |  | : $csv->getline_all ($fh); | 
| 1500 | 248 | 50 |  |  |  | 577 | if ($ref) { | 
| 1501 | 248 | 100 | 66 |  |  | 1127 | @row1 && !$c->{'hd_c'} && !ref $hdrs and unshift @{$ref}, \@row1; | 
|  | 4 |  | 100 |  |  | 10 |  | 
| 1502 |  |  |  |  |  |  | } | 
| 1503 |  |  |  |  |  |  | else { | 
| 1504 | 0 |  |  |  |  | 0 | Text::CSV_XS->auto_diag (); | 
| 1505 |  |  |  |  |  |  | } | 
| 1506 | 248 | 100 |  |  |  | 3158 | $c->{'cls'} and close $fh; | 
| 1507 | 248 | 100 | 100 |  |  | 1489 | if ($ref and $c->{'cbai'} || $c->{'cboi'}) { | 
|  |  |  | 66 |  |  |  |  | 
| 1508 |  |  |  |  |  |  | # Default is ARRAYref, but with key =>, you'll get a hashref | 
| 1509 | 22 | 100 |  |  |  | 67 | foreach my $r (ref $ref eq "ARRAY" ? @{$ref} : values %{$ref}) { | 
|  | 21 |  |  |  |  | 47 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 1510 | 71 |  |  |  |  | 6741 | local %_; | 
| 1511 | 71 | 100 |  |  |  | 172 | ref $r eq "HASH" and *_ = $r; | 
| 1512 | 71 | 100 |  |  |  | 182 | $c->{'cbai'} and $c->{'cbai'}->($csv, $r); | 
| 1513 | 71 | 100 |  |  |  | 4197 | $c->{'cboi'} and $c->{'cboi'}->($csv, $r); | 
| 1514 |  |  |  |  |  |  | } | 
| 1515 |  |  |  |  |  |  | } | 
| 1516 |  |  |  |  |  |  |  | 
| 1517 | 248 | 100 |  |  |  | 1851 | if ($c->{'sink'}) { | 
| 1518 | 6 | 50 |  |  |  | 26 | my $ro = ref $c->{'out'} or return; | 
| 1519 |  |  |  |  |  |  |  | 
| 1520 | 6 | 100 | 66 |  |  | 19 | $ro eq "SCALAR" && ${$c->{'out'}} eq "skip" and | 
|  | 1 |  |  |  |  | 15 |  | 
| 1521 |  |  |  |  |  |  | return; | 
| 1522 |  |  |  |  |  |  |  | 
| 1523 | 5 | 50 |  |  |  | 10 | $ro eq ref $ref or | 
| 1524 |  |  |  |  |  |  | croak ($csv->_SetDiagInfo (5001, "Output type mismatch")); | 
| 1525 |  |  |  |  |  |  |  | 
| 1526 | 5 | 100 |  |  |  | 12 | if ($ro eq "ARRAY") { | 
| 1527 | 4 | 100 | 33 |  |  | 6 | if (@{$c->{'out'}} and @$ref and ref $c->{'out'}[0] eq ref $ref->[0]) { | 
|  | 4 |  | 66 |  |  | 25 |  | 
| 1528 | 2 |  |  |  |  | 4 | push @{$c->{'out'}} => @$ref; | 
|  | 2 |  |  |  |  | 10 |  | 
| 1529 | 2 |  |  |  |  | 36 | return $c->{'out'}; | 
| 1530 |  |  |  |  |  |  | } | 
| 1531 | 2 |  |  |  |  | 7 | croak ($csv->_SetDiagInfo (5001, "Output type mismatch")); | 
| 1532 |  |  |  |  |  |  | } | 
| 1533 |  |  |  |  |  |  |  | 
| 1534 | 1 | 50 |  |  |  | 5 | if ($ro eq "HASH") { | 
| 1535 | 1 |  |  |  |  | 3 | @{$c->{'out'}}{keys %{$ref}} = values %{$ref}; | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 3 |  | 
|  | 1 |  |  |  |  | 4 |  | 
| 1536 | 1 |  |  |  |  | 15 | return $c->{'out'}; | 
| 1537 |  |  |  |  |  |  | } | 
| 1538 |  |  |  |  |  |  |  | 
| 1539 | 0 |  |  |  |  | 0 | croak ($csv->_SetDiagInfo (5002, "Unsupported output type")); | 
| 1540 |  |  |  |  |  |  | } | 
| 1541 |  |  |  |  |  |  |  | 
| 1542 |  |  |  |  |  |  | defined wantarray or | 
| 1543 |  |  |  |  |  |  | return csv ( | 
| 1544 |  |  |  |  |  |  | 'in'      => $ref, | 
| 1545 |  |  |  |  |  |  | 'headers' => $hdrs, | 
| 1546 | 242 | 100 |  |  |  | 534 | %{$c->{'attr'}}, | 
|  | 1 |  |  |  |  | 17 |  | 
| 1547 |  |  |  |  |  |  | ); | 
| 1548 |  |  |  |  |  |  |  | 
| 1549 | 241 |  |  |  |  | 3702 | return $ref; | 
| 1550 |  |  |  |  |  |  | } # csv | 
| 1551 |  |  |  |  |  |  |  | 
| 1552 |  |  |  |  |  |  | 1; | 
| 1553 |  |  |  |  |  |  |  | 
| 1554 |  |  |  |  |  |  | __END__ |