summaryrefslogtreecommitdiff
path: root/cpan
diff options
context:
space:
mode:
authorfukachan <fukachan>2001-09-24 02:55:28 +0000
committerfukachan <fukachan>2001-09-24 02:55:28 +0000
commit92e21ffd3554016337d0c0dd3207a58090b22887 (patch)
treeb8f5bfa475ed60773a27a2b3adf41b7896576669 /cpan
parent69784352d8270419ec5395543c5f6f0f0d5dcdc3 (diff)
downloadfml8-92e21ffd3554016337d0c0dd3207a58090b22887.tar.gz
fml8-92e21ffd3554016337d0c0dd3207a58090b22887.tar.bz2
fml8-92e21ffd3554016337d0c0dd3207a58090b22887.zip
Initial revision
Diffstat (limited to 'cpan')
-rw-r--r--cpan/dist/Text-CSV/CSV.pm488
-rw-r--r--cpan/dist/Text-CSV/MANIFEST5
-rw-r--r--cpan/dist/Text-CSV/Makefile.PL7
-rw-r--r--cpan/dist/Text-CSV/README32
-rw-r--r--cpan/dist/Text-CSV/test.pl131
-rw-r--r--cpan/lib/Text/CSV.pm488
6 files changed, 1151 insertions, 0 deletions
diff --git a/cpan/dist/Text-CSV/CSV.pm b/cpan/dist/Text-CSV/CSV.pm
new file mode 100644
index 00000000..bb03b2a0
--- /dev/null
+++ b/cpan/dist/Text-CSV/CSV.pm
@@ -0,0 +1,488 @@
+package Text::CSV;
+
+# Copyright (c) 1997 Alan Citterman. All rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+################################################################################
+# HISTORY
+#
+# Written by:
+# Alan Citterman <alan@mfgrtl.com>
+#
+# Version 0.01 06/05/1997
+# original version
+################################################################################
+
+require 5.002;
+
+use strict;
+
+BEGIN {
+ use Exporter ();
+ use AutoLoader qw(AUTOLOAD);
+ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+ $VERSION = '0.01';
+ @ISA = qw(Exporter AutoLoader);
+ @EXPORT = qw();
+ @EXPORT_OK = qw();
+ %EXPORT_TAGS = qw();
+}
+
+1;
+
+__END__
+
+################################################################################
+# version
+#
+# class/object method expecting no arguments and returning the version number
+# of Text::CSV. there are no side-effects.
+################################################################################
+sub version {
+ return $VERSION;
+}
+
+################################################################################
+# new
+#
+# class/object method expecting no arguments and returning a reference to a
+# newly created Text::CSV object.
+################################################################################
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ $self->{'_STATUS'} = undef;
+ $self->{'_ERROR_INPUT'} = undef;
+ $self->{'_STRING'} = undef;
+ $self->{'_FIELDS'} = undef;
+ bless $self, $class;
+ return $self;
+}
+
+################################################################################
+# status
+#
+# object method returning the success or failure of the most recent combine()
+# or parse(). there are no side-effects.
+################################################################################
+sub status {
+ my $self = shift;
+ return $self->{'_STATUS'};
+}
+
+################################################################################
+# error_input
+#
+# object method returning the first invalid argument to the most recent
+# combine() or parse(). there are no side-effects.
+################################################################################
+sub error_input {
+ my $self = shift;
+ return $self->{'_ERROR_INPUT'};
+}
+
+################################################################################
+# string
+#
+# object method returning the result of the most recent combine() or the
+# input to the most recent parse(), whichever is more recent. there are no
+# side-effects.
+################################################################################
+sub string {
+ my $self = shift;
+ return $self->{'_STRING'};
+}
+
+################################################################################
+# fields
+#
+# object method returning the result of the most recent parse() or the input
+# to the most recent combine(), whichever is more recent. there are no
+# side-effects.
+################################################################################
+sub fields {
+ my $self = shift;
+ if (ref($self->{'_FIELDS'})) {
+ return @{$self->{'_FIELDS'}};
+ }
+ return undef;
+}
+
+################################################################################
+# combine
+#
+# object method returning success or failure. the given arguments are
+# combined into a single comma-separated value. failure can be the result of
+# no arguments or an argument containing an invalid character. side-effects
+# include:
+# setting status()
+# setting fields()
+# setting string()
+# setting error_input()
+################################################################################
+sub combine {
+ my $self = shift;
+ my @part = @_;
+ $self->{'_FIELDS'} = \@part;
+ $self->{'_ERROR_INPUT'} = undef;
+ $self->{'_STATUS'} = 0;
+ $self->{'_STRING'} = '';
+ my $column = '';
+ my $combination = '';
+ my $skip_comma = 1;
+ if ($#part >= 0) {
+
+ # at least one argument was given for "combining"...
+ for $column (@part) {
+ if ($column =~ /[^\t\040-\176]/) {
+
+ # an argument contained an invalid character...
+ $self->{'_ERROR_INPUT'} = $column;
+ return $self->{'_STATUS'};
+ }
+ if ($skip_comma) {
+
+ # do not put a comma before the first argument...
+ $skip_comma = 0;
+ } else {
+
+ # do put a comma before all arguments except the first argument...
+ $combination .= ',';
+ }
+ $column =~ s/\042/\042\042/go;
+ $combination .= "\042";
+ $combination .= $column;
+ $combination .= "\042";
+ }
+ $self->{'_STRING'} = $combination;
+ $self->{'_STATUS'} = 1;
+ }
+ return $self->{'_STATUS'};
+}
+
+################################################################################
+# parse
+#
+# object method returning success or failure. the given argument is expected
+# to be a valid comma-separated value. failure can be the result of
+# no arguments or an argument containing an invalid sequence of characters.
+# side-effects include:
+# setting status()
+# setting fields()
+# setting string()
+# setting error_input()
+################################################################################
+sub parse {
+ my $self = shift;
+ $self->{'_STRING'} = shift;
+ $self->{'_FIELDS'} = undef;
+ $self->{'_ERROR_INPUT'} = $self->{'_STRING'};
+ $self->{'_STATUS'} = 0;
+ if (!defined($self->{'_STRING'})) {
+ return $self->{'_STATUS'};
+ }
+ my $keep_biting = 1;
+ my $palatable = 0;
+ my $line = $self->{'_STRING'};
+ if ($line =~ /\n$/) {
+ chop($line);
+ if ($line =~ /\r$/) {
+ chop($line);
+ }
+ }
+ my $mouthful = '';
+ my @part = ();
+ while ($keep_biting and ($palatable = $self->_bite(\$line, \$mouthful, \$keep_biting))) {
+ push(@part, $mouthful);
+ }
+ if ($palatable) {
+ $self->{'_ERROR_INPUT'} = undef;
+ $self->{'_FIELDS'} = \@part;
+ }
+ return $self->{'_STATUS'} = $palatable;
+}
+
+################################################################################
+# _bite
+#
+# *private* class/object method returning success or failure. the arguments
+# are:
+# - a reference to a comma-separated value string
+# - a reference to a return string
+# - a reference to a return boolean
+# upon success the first comma-separated value of the csv string is
+# transferred to the return string and the boolean is set to true if a comma
+# followed that value. in other words, "bite" one value off of csv
+# returning the remaining string, the "piece" bitten, and if there's any
+# more. failure can be the result of the csv string containing an invalid
+# sequence of characters.
+#
+# from the csv string and
+# to be a valid comma-separated value. failure can be the result of
+# no arguments or an argument containing an invalid sequence of characters.
+# side-effects include:
+# setting status()
+# setting fields()
+# setting string()
+# setting error_input()
+################################################################################
+sub _bite {
+ my ($self, $line_ref, $piece_ref, $bite_again_ref) = @_;
+ my $in_quotes = 0;
+ my $ok = 0;
+ $$piece_ref = '';
+ $$bite_again_ref = 0;
+ while (1) {
+ if (length($$line_ref) < 1) {
+
+ # end of string...
+ if ($in_quotes) {
+
+ # end of string, missing closing double-quote...
+ last;
+ } else {
+
+ # proper end of string...
+ $ok = 1;
+ last;
+ }
+ } elsif ($$line_ref =~ /^\042/) {
+
+ # double-quote...
+ if ($in_quotes) {
+ if (length($$line_ref) == 1) {
+
+ # closing double-quote at end of string...
+ substr($$line_ref, 0, 1) = '';
+ $ok = 1;
+ last;
+ } elsif ($$line_ref =~ /^\042\042/) {
+
+ # an embedded double-quote...
+ $$piece_ref .= "\042";
+ substr($$line_ref, 0, 2) = '';
+ } elsif ($$line_ref =~ /^\042,/) {
+
+ # closing double-quote followed by a comma...
+ substr($$line_ref, 0, 2) = '';
+ $$bite_again_ref = 1;
+ $ok = 1;
+ last;
+ } else {
+
+ # double-quote, followed by undesirable character (bad character sequence)...
+ last;
+ }
+ } else {
+ if (length($$piece_ref) < 1) {
+
+ # starting double-quote at beginning of string
+ $in_quotes = 1;
+ substr($$line_ref, 0, 1) = '';
+ } else {
+
+ # double-quote, outside of double-quotes (bad character sequence)...
+ last;
+ }
+ }
+ } elsif ($$line_ref =~ /^,/) {
+
+ # comma...
+ if ($in_quotes) {
+
+ # a comma, inside double-quotes...
+ $$piece_ref .= substr($$line_ref, 0 ,1);
+ substr($$line_ref, 0, 1) = '';
+ } else {
+
+ # a comma, which separates values...
+ substr($$line_ref, 0, 1) = '';
+ $$bite_again_ref = 1;
+ $ok = 1;
+ last;
+ }
+ } elsif ($$line_ref =~ /^[\t\040-\176]/) {
+
+ # a tab, space, or printable...
+ $$piece_ref .= substr($$line_ref, 0 ,1);
+ substr($$line_ref, 0, 1) = '';
+ } else {
+
+ # an undesirable character...
+ last;
+ }
+ }
+ return $ok;
+}
+
+=head1 NAME
+
+Text::CSV - comma-separated values manipulation routines
+
+=head1 SYNOPSIS
+
+ use Text::CSV;
+
+ $version = Text::CSV->version(); # get the module version
+
+ $csv = Text::CSV->new(); # create a new object
+
+ $status = $csv->combine(@columns); # combine columns into a string
+ $line = $csv->string(); # get the combined string
+
+ $status = $csv->parse($line); # parse a CSV string into fields
+ @columns = $csv->fields(); # get the parsed fields
+
+ $status = $csv->status(); # get the most recent status
+ $bad_argument = $csv->error_input(); # get the most recent bad argument
+
+=head1 DESCRIPTION
+
+Text::CSV provides facilities for the composition and decomposition of
+comma-separated values. An instance of the Text::CSV class can combine
+fields into a CSV string and parse a CSV string into fields.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item version
+
+ $version = Text::CSV->version();
+
+This function may be called as a class or an object method. It returns the current
+module version.
+
+=item new
+
+ $csv = Text::CSV->new();
+
+This function may be called as a class or an object method. It returns a reference to a
+newly created Text::CSV object.
+
+=item combine
+
+ $status = $csv->combine(@columns);
+
+This object function constructs a CSV string from the arguments, returning
+success or failure. Failure can result from lack of arguments or an argument
+containing an invalid character. Upon success, C<string()> can be called to
+retrieve the resultant CSV string. Upon failure, the value returned by
+C<string()> is undefined and C<error_input()> can be called to retrieve an
+invalid argument.
+
+=item string
+
+ $line = $csv->string();
+
+This object function returns the input to C<parse()> or the resultant CSV string of
+C<combine()>, whichever was called more recently.
+
+=item parse
+
+ $status = $csv->parse($line);
+
+This object function decomposes a CSV string into fields, returning
+success or failure. Failure can result from a lack of argument or the given CSV
+string is improperly formatted. Upon success, C<fields()> can be called to
+retrieve the decomposed fields . Upon failure, the value returned by
+C<fields()> is undefined and C<error_input()> can be called to retrieve the
+invalid argument.
+
+=item fields
+
+ @columns = $csv->fields();
+
+This object function returns the input to C<combine()> or the resultant decomposed
+fields of C<parse()>, whichever was called more recently.
+
+=item status
+
+ $status = $csv->status();
+
+This object function returns success (or failure) of C<combine()> or C<parse()>,
+whichever was called more recently.
+
+=item error_input
+
+ $bad_argument = $csv->error_input();
+
+This object function returns the erroneous argument (if it exists) of C<combine()>
+or C<parse()>, whichever was called more recently.
+
+=back
+
+=head1 EXAMPLE
+
+ require Text::CSV;
+
+ my $csv = Text::CSV->new;
+
+ my $column = '';
+ my $sample_input_string = '"I said, ""Hi!""",Yes,"",2.34,,"1.09"';
+ if ($csv->parse($sample_input_string)) {
+ my @field = $csv->fields;
+ my $count = 0;
+ for $column (@field) {
+ print ++$count, " => ", $column, "\n";
+ }
+ print "\n";
+ } else {
+ my $err = $csv->error_input;
+ print "parse() failed on argument: ", $err, "\n";
+ }
+
+ my @sample_input_fields = ('You said, "Hello!"',
+ 5.67,
+ 'Surely',
+ '',
+ '3.14159');
+ if ($csv->combine(@sample_input_fields)) {
+ my $string = $csv->string;
+ print $string, "\n";
+ } else {
+ my $err = $csv->error_input;
+ print "combine() failed on argument: ", $err, "\n";
+ }
+
+=head1 CAVEATS
+
+This module is based upon a working definition of CSV format which may not be
+the most general.
+
+=over 4
+
+=item 1
+
+Allowable characters within a CSV field include 0x09 (tab) and the inclusive
+range of 0x20 (space) through 0x7E (tilde).
+
+=item 2
+
+A field within CSV may be surrounded by double-quotes.
+
+=item 3
+
+A field within CSV must be surrounded by double-quotes to contain a comma.
+
+=item 4
+
+A field within CSV must be surrounded by double-quotes to contain an embedded
+double-quote, represented by a pair of consecutive double-quotes.
+
+=item 5
+
+A CSV string may be terminated by 0x0A (line feed) or by 0x0D,0x0A
+(carriage return, line feed).
+
+=head1 AUTHOR
+
+Alan Citterman F<E<lt>alan@mfgrtl.comE<gt>>
+
+=head1 SEE ALSO
+
+perl(1)
+
+=cut
diff --git a/cpan/dist/Text-CSV/MANIFEST b/cpan/dist/Text-CSV/MANIFEST
new file mode 100644
index 00000000..19df7eeb
--- /dev/null
+++ b/cpan/dist/Text-CSV/MANIFEST
@@ -0,0 +1,5 @@
+README
+MANIFEST
+CSV.pm
+Makefile.PL
+test.pl
diff --git a/cpan/dist/Text-CSV/Makefile.PL b/cpan/dist/Text-CSV/Makefile.PL
new file mode 100644
index 00000000..c6bfa159
--- /dev/null
+++ b/cpan/dist/Text-CSV/Makefile.PL
@@ -0,0 +1,7 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+ 'NAME' => 'Text::CSV',
+ 'VERSION_FROM' => 'CSV.pm', # finds $VERSION
+);
diff --git a/cpan/dist/Text-CSV/README b/cpan/dist/Text-CSV/README
new file mode 100644
index 00000000..e25e4cda
--- /dev/null
+++ b/cpan/dist/Text-CSV/README
@@ -0,0 +1,32 @@
+Module: Text::CSV
+
+Description:
+ Text::CSV provides facilities for the composition and decomposition of
+ comma-separated values. An instance of the Text::CSV class can combine
+ fields into a CSV string and parse a CSV string into fields.
+
+Copying:
+ Copyright (c) 1997 Alan Citterman. All rights reserved. This program is
+ free software; you can redistribute it and/or modify it under the same
+ terms as Perl itself.
+
+Prerequisites:
+ perl 5.002
+
+Build/Installation:
+ Standard build/installation supported by ExtUtils::MakeMaker(3)...
+ perl Makefile.PL
+ make
+ make test
+ make install
+
+Recent Changes:
+ Version 0.01 06/05/1997
+ original version
+
+Planned Enhancements:
+ + extend constructor to accept parse() configuration to support other
+ variations of CSV
+
+Author:
+ Alan Citterman <alan@mfgrtl.com>
diff --git a/cpan/dist/Text-CSV/test.pl b/cpan/dist/Text-CSV/test.pl
new file mode 100644
index 00000000..f15dfd3b
--- /dev/null
+++ b/cpan/dist/Text-CSV/test.pl
@@ -0,0 +1,131 @@
+# Before `make install' is performed this script should be runnable with
+# `make test'. After `make install' it should work as `perl test.pl'
+
+######################### We start with some black magic to print on failure.
+
+# Change 1..1 below to 1..last_test_to_print .
+# (It may become useful if the test is moved to ./t subdirectory.)
+
+BEGIN { $| = 1; print "1..20\n"; }
+END {print "not ok 1\n" unless $loaded;}
+use Text::CSV;
+$loaded = 1;
+print "ok 1\n";
+
+######################### End of black magic.
+
+# Insert your test code below (better if it prints "ok 13"
+# (correspondingly "not ok 13") depending on the success of chunk 13
+# of the test code):
+
+#
+# empty subclass test
+#
+package Empty_Subclass;
+@ISA = qw(Text::CSV);
+package main;
+my $empty = Empty_Subclass->new();
+if ($empty->version() and $empty->parse('') and $empty->combine('')) {
+ print "ok 2\n";
+} else {
+ print "not ok 2\n";
+}
+
+my $csv = Text::CSV->new();
+
+if (! $csv->combine()) { # fail - missing argument
+ print "ok 3\n";
+} else {
+ print "not ok 3\n";
+}
+if (! $csv->combine('abc', "def\n", 'ghi')) { # fail - bad character
+ print "ok 4\n";
+} else {
+ print "not ok 4\n";
+}
+if ($csv->combine('') && ($csv->string eq q(""))) { # succeed
+ print "ok 5\n";
+} else {
+ print "not ok 5\n";
+}
+if ($csv->combine('', '') && ($csv->string eq q("",""))) { # succeed
+ print "ok 6\n";
+} else {
+ print "not ok 6\n";
+}
+if ($csv->combine('', 'I said, "Hi!"', '') &&
+ ($csv->string eq q("","I said, ""Hi!""",""))) { # succeed
+ print "ok 7\n";
+} else {
+ print "not ok 7\n";
+}
+if ($csv->combine('"', 'abc') && ($csv->string eq q("""","abc"))) { # succeed
+ print "ok 8\n";
+} else {
+ print "not ok 8\n";
+}
+if ($csv->combine('abc', '"') && ($csv->string eq q("abc",""""))) { # succeed
+ print "ok 9\n";
+} else {
+ print "not ok 9\n";
+}
+if ($csv->combine('abc', 'def', 'ghi') &&
+ ($csv->string eq q("abc","def","ghi"))) { # succeed
+ print "ok 10\n";
+} else {
+ print "not ok 10\n";
+}
+if ($csv->combine("abc\tdef", 'ghi') &&
+ ($csv->string eq qq("abc\tdef","ghi"))) { # succeed
+ print "ok 11\n";
+} else {
+ print "not ok 11\n";
+}
+if (! $csv->parse()) { # fail - missing argument
+ print "ok 12\n";
+} else {
+ print "not ok 12\n";
+}
+if (! $csv->parse('"abc')) { # fail - missing closing double-quote
+ print "ok 13\n";
+} else {
+ print "not ok 13\n";
+}
+if (! $csv->parse('ab"c')) { # fail - double-quote outside of double-quotes
+ print "ok 14\n";
+} else {
+ print "not ok 14\n";
+}
+if (! $csv->parse('"ab"c"')) { # fail - bad character sequence
+ print "ok 15\n";
+} else {
+ print "not ok 15\n";
+}
+if (! $csv->parse(qq("abc\nc"))) { # fail - bad character
+ print "ok 16\n";
+} else {
+ print "not ok 16\n";
+}
+if (! $csv->status()) { # fail - test #16 should have failed
+ print "ok 17\n";
+} else {
+ print "not ok 17\n";
+}
+if ($csv->parse(q(",")) and ($csv->fields())[0] eq ',') { # success
+ print "ok 18\n";
+} else {
+ print "not ok 18\n";
+}
+if ($csv->parse(qq("","I said,\t""Hi!""","")) and
+($csv->fields())[0] eq '' and
+($csv->fields())[1] eq qq(I said,\t"Hi!") and
+($csv->fields())[2] eq '') { # success
+ print "ok 19\n";
+} else {
+ print "not ok 19\n";
+}
+if ($csv->status()) { # success - test #19 should have succeeded
+ print "ok 20\n";
+} else {
+ print "not ok 20\n";
+}
diff --git a/cpan/lib/Text/CSV.pm b/cpan/lib/Text/CSV.pm
new file mode 100644
index 00000000..bb03b2a0
--- /dev/null
+++ b/cpan/lib/Text/CSV.pm
@@ -0,0 +1,488 @@
+package Text::CSV;
+
+# Copyright (c) 1997 Alan Citterman. All rights reserved.
+# This program is free software; you can redistribute it and/or
+# modify it under the same terms as Perl itself.
+
+################################################################################
+# HISTORY
+#
+# Written by:
+# Alan Citterman <alan@mfgrtl.com>
+#
+# Version 0.01 06/05/1997
+# original version
+################################################################################
+
+require 5.002;
+
+use strict;
+
+BEGIN {
+ use Exporter ();
+ use AutoLoader qw(AUTOLOAD);
+ use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
+ $VERSION = '0.01';
+ @ISA = qw(Exporter AutoLoader);
+ @EXPORT = qw();
+ @EXPORT_OK = qw();
+ %EXPORT_TAGS = qw();
+}
+
+1;
+
+__END__
+
+################################################################################
+# version
+#
+# class/object method expecting no arguments and returning the version number
+# of Text::CSV. there are no side-effects.
+################################################################################
+sub version {
+ return $VERSION;
+}
+
+################################################################################
+# new
+#
+# class/object method expecting no arguments and returning a reference to a
+# newly created Text::CSV object.
+################################################################################
+sub new {
+ my $proto = shift;
+ my $class = ref($proto) || $proto;
+ my $self = {};
+ $self->{'_STATUS'} = undef;
+ $self->{'_ERROR_INPUT'} = undef;
+ $self->{'_STRING'} = undef;
+ $self->{'_FIELDS'} = undef;
+ bless $self, $class;
+ return $self;
+}
+
+################################################################################
+# status
+#
+# object method returning the success or failure of the most recent combine()
+# or parse(). there are no side-effects.
+################################################################################
+sub status {
+ my $self = shift;
+ return $self->{'_STATUS'};
+}
+
+################################################################################
+# error_input
+#
+# object method returning the first invalid argument to the most recent
+# combine() or parse(). there are no side-effects.
+################################################################################
+sub error_input {
+ my $self = shift;
+ return $self->{'_ERROR_INPUT'};
+}
+
+################################################################################
+# string
+#
+# object method returning the result of the most recent combine() or the
+# input to the most recent parse(), whichever is more recent. there are no
+# side-effects.
+################################################################################
+sub string {
+ my $self = shift;
+ return $self->{'_STRING'};
+}
+
+################################################################################
+# fields
+#
+# object method returning the result of the most recent parse() or the input
+# to the most recent combine(), whichever is more recent. there are no
+# side-effects.
+################################################################################
+sub fields {
+ my $self = shift;
+ if (ref($self->{'_FIELDS'})) {
+ return @{$self->{'_FIELDS'}};
+ }
+ return undef;
+}
+
+################################################################################
+# combine
+#
+# object method returning success or failure. the given arguments are
+# combined into a single comma-separated value. failure can be the result of
+# no arguments or an argument containing an invalid character. side-effects
+# include:
+# setting status()
+# setting fields()
+# setting string()
+# setting error_input()
+################################################################################
+sub combine {
+ my $self = shift;
+ my @part = @_;
+ $self->{'_FIELDS'} = \@part;
+ $self->{'_ERROR_INPUT'} = undef;
+ $self->{'_STATUS'} = 0;
+ $self->{'_STRING'} = '';
+ my $column = '';
+ my $combination = '';
+ my $skip_comma = 1;
+ if ($#part >= 0) {
+
+ # at least one argument was given for "combining"...
+ for $column (@part) {
+ if ($column =~ /[^\t\040-\176]/) {
+
+ # an argument contained an invalid character...
+ $self->{'_ERROR_INPUT'} = $column;
+ return $self->{'_STATUS'};
+ }
+ if ($skip_comma) {
+
+ # do not put a comma before the first argument...
+ $skip_comma = 0;
+ } else {
+
+ # do put a comma before all arguments except the first argument...
+ $combination .= ',';
+ }
+ $column =~ s/\042/\042\042/go;
+ $combination .= "\042";
+ $combination .= $column;
+ $combination .= "\042";
+ }
+ $self->{'_STRING'} = $combination;
+ $self->{'_STATUS'} = 1;
+ }
+ return $self->{'_STATUS'};
+}
+
+################################################################################
+# parse
+#
+# object method returning success or failure. the given argument is expected
+# to be a valid comma-separated value. failure can be the result of
+# no arguments or an argument containing an invalid sequence of characters.
+# side-effects include:
+# setting status()
+# setting fields()
+# setting string()
+# setting error_input()
+################################################################################
+sub parse {
+ my $self = shift;
+ $self->{'_STRING'} = shift;
+ $self->{'_FIELDS'} = undef;
+ $self->{'_ERROR_INPUT'} = $self->{'_STRING'};
+ $self->{'_STATUS'} = 0;
+ if (!defined($self->{'_STRING'})) {
+ return $self->{'_STATUS'};
+ }
+ my $keep_biting = 1;
+ my $palatable = 0;
+ my $line = $self->{'_STRING'};
+ if ($line =~ /\n$/) {
+ chop($line);
+ if ($line =~ /\r$/) {
+ chop($line);
+ }
+ }
+ my $mouthful = '';
+ my @part = ();
+ while ($keep_biting and ($palatable = $self->_bite(\$line, \$mouthful, \$keep_biting))) {
+ push(@part, $mouthful);
+ }
+ if ($palatable) {
+ $self->{'_ERROR_INPUT'} = undef;
+ $self->{'_FIELDS'} = \@part;
+ }
+ return $self->{'_STATUS'} = $palatable;
+}
+
+################################################################################
+# _bite
+#
+# *private* class/object method returning success or failure. the arguments
+# are:
+# - a reference to a comma-separated value string
+# - a reference to a return string
+# - a reference to a return boolean
+# upon success the first comma-separated value of the csv string is
+# transferred to the return string and the boolean is set to true if a comma
+# followed that value. in other words, "bite" one value off of csv
+# returning the remaining string, the "piece" bitten, and if there's any
+# more. failure can be the result of the csv string containing an invalid
+# sequence of characters.
+#
+# from the csv string and
+# to be a valid comma-separated value. failure can be the result of
+# no arguments or an argument containing an invalid sequence of characters.
+# side-effects include:
+# setting status()
+# setting fields()
+# setting string()
+# setting error_input()
+################################################################################
+sub _bite {
+ my ($self, $line_ref, $piece_ref, $bite_again_ref) = @_;
+ my $in_quotes = 0;
+ my $ok = 0;
+ $$piece_ref = '';
+ $$bite_again_ref = 0;
+ while (1) {
+ if (length($$line_ref) < 1) {
+
+ # end of string...
+ if ($in_quotes) {
+
+ # end of string, missing closing double-quote...
+ last;
+ } else {
+
+ # proper end of string...
+ $ok = 1;
+ last;
+ }
+ } elsif ($$line_ref =~ /^\042/) {
+
+ # double-quote...
+ if ($in_quotes) {
+ if (length($$line_ref) == 1) {
+
+ # closing double-quote at end of string...
+ substr($$line_ref, 0, 1) = '';
+ $ok = 1;
+ last;
+ } elsif ($$line_ref =~ /^\042\042/) {
+
+ # an embedded double-quote...
+ $$piece_ref .= "\042";
+ substr($$line_ref, 0, 2) = '';
+ } elsif ($$line_ref =~ /^\042,/) {
+
+ # closing double-quote followed by a comma...
+ substr($$line_ref, 0, 2) = '';
+ $$bite_again_ref = 1;
+ $ok = 1;
+ last;
+ } else {
+
+ # double-quote, followed by undesirable character (bad character sequence)...
+ last;
+ }
+ } else {
+ if (length($$piece_ref) < 1) {
+
+ # starting double-quote at beginning of string
+ $in_quotes = 1;
+ substr($$line_ref, 0, 1) = '';
+ } else {
+
+ # double-quote, outside of double-quotes (bad character sequence)...
+ last;
+ }
+ }
+ } elsif ($$line_ref =~ /^,/) {
+
+ # comma...
+ if ($in_quotes) {
+
+ # a comma, inside double-quotes...
+ $$piece_ref .= substr($$line_ref, 0 ,1);
+ substr($$line_ref, 0, 1) = '';
+ } else {
+
+ # a comma, which separates values...
+ substr($$line_ref, 0, 1) = '';
+ $$bite_again_ref = 1;
+ $ok = 1;
+ last;
+ }
+ } elsif ($$line_ref =~ /^[\t\040-\176]/) {
+
+ # a tab, space, or printable...
+ $$piece_ref .= substr($$line_ref, 0 ,1);
+ substr($$line_ref, 0, 1) = '';
+ } else {
+
+ # an undesirable character...
+ last;
+ }
+ }
+ return $ok;
+}
+
+=head1 NAME
+
+Text::CSV - comma-separated values manipulation routines
+
+=head1 SYNOPSIS
+
+ use Text::CSV;
+
+ $version = Text::CSV->version(); # get the module version
+
+ $csv = Text::CSV->new(); # create a new object
+
+ $status = $csv->combine(@columns); # combine columns into a string
+ $line = $csv->string(); # get the combined string
+
+ $status = $csv->parse($line); # parse a CSV string into fields
+ @columns = $csv->fields(); # get the parsed fields
+
+ $status = $csv->status(); # get the most recent status
+ $bad_argument = $csv->error_input(); # get the most recent bad argument
+
+=head1 DESCRIPTION
+
+Text::CSV provides facilities for the composition and decomposition of
+comma-separated values. An instance of the Text::CSV class can combine
+fields into a CSV string and parse a CSV string into fields.
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item version
+
+ $version = Text::CSV->version();
+
+This function may be called as a class or an object method. It returns the current
+module version.
+
+=item new
+
+ $csv = Text::CSV->new();
+
+This function may be called as a class or an object method. It returns a reference to a
+newly created Text::CSV object.
+
+=item combine
+
+ $status = $csv->combine(@columns);
+
+This object function constructs a CSV string from the arguments, returning
+success or failure. Failure can result from lack of arguments or an argument
+containing an invalid character. Upon success, C<string()> can be called to
+retrieve the resultant CSV string. Upon failure, the value returned by
+C<string()> is undefined and C<error_input()> can be called to retrieve an
+invalid argument.
+
+=item string
+
+ $line = $csv->string();
+
+This object function returns the input to C<parse()> or the resultant CSV string of
+C<combine()>, whichever was called more recently.
+
+=item parse
+
+ $status = $csv->parse($line);
+
+This object function decomposes a CSV string into fields, returning
+success or failure. Failure can result from a lack of argument or the given CSV
+string is improperly formatted. Upon success, C<fields()> can be called to
+retrieve the decomposed fields . Upon failure, the value returned by
+C<fields()> is undefined and C<error_input()> can be called to retrieve the
+invalid argument.
+
+=item fields
+
+ @columns = $csv->fields();
+
+This object function returns the input to C<combine()> or the resultant decomposed
+fields of C<parse()>, whichever was called more recently.
+
+=item status
+
+ $status = $csv->status();
+
+This object function returns success (or failure) of C<combine()> or C<parse()>,
+whichever was called more recently.
+
+=item error_input
+
+ $bad_argument = $csv->error_input();
+
+This object function returns the erroneous argument (if it exists) of C<combine()>
+or C<parse()>, whichever was called more recently.
+
+=back
+
+=head1 EXAMPLE
+
+ require Text::CSV;
+
+ my $csv = Text::CSV->new;
+
+ my $column = '';
+ my $sample_input_string = '"I said, ""Hi!""",Yes,"",2.34,,"1.09"';
+ if ($csv->parse($sample_input_string)) {
+ my @field = $csv->fields;
+ my $count = 0;
+ for $column (@field) {
+ print ++$count, " => ", $column, "\n";
+ }
+ print "\n";
+ } else {
+ my $err = $csv->error_input;
+ print "parse() failed on argument: ", $err, "\n";
+ }
+
+ my @sample_input_fields = ('You said, "Hello!"',
+ 5.67,
+ 'Surely',
+ '',
+ '3.14159');
+ if ($csv->combine(@sample_input_fields)) {
+ my $string = $csv->string;
+ print $string, "\n";
+ } else {
+ my $err = $csv->error_input;
+ print "combine() failed on argument: ", $err, "\n";
+ }
+
+=head1 CAVEATS
+
+This module is based upon a working definition of CSV format which may not be
+the most general.
+
+=over 4
+
+=item 1
+
+Allowable characters within a CSV field include 0x09 (tab) and the inclusive
+range of 0x20 (space) through 0x7E (tilde).
+
+=item 2
+
+A field within CSV may be surrounded by double-quotes.
+
+=item 3
+
+A field within CSV must be surrounded by double-quotes to contain a comma.
+
+=item 4
+
+A field within CSV must be surrounded by double-quotes to contain an embedded
+double-quote, represented by a pair of consecutive double-quotes.
+
+=item 5
+
+A CSV string may be terminated by 0x0A (line feed) or by 0x0D,0x0A
+(carriage return, line feed).
+
+=head1 AUTHOR
+
+Alan Citterman F<E<lt>alan@mfgrtl.comE<gt>>
+
+=head1 SEE ALSO
+
+perl(1)
+
+=cut