package Convert::yEnc::RC; use strict; use Convert::yEnc::Entry; use warnings; use overload 'eq' => \&_eq; sub new { my ($class, $file) = @_; my $rc = { }; bless $rc, $class; $rc->load($file) if $file; $rc } sub load { my($rc, $file) = @_; $rc->{file } = $file; $rc->{db } = { }; no warnings qw(uninitialized); open RC, $file or return undef; while (my $line = ) { $line =~ /\S/ or next; my($name, $rest) = split "\t", $line, 2; my $entry = load Convert::yEnc::Entry $rest; $rc->{db}{$name} = $entry; } close(RC); 1 } sub update { my($rc, $line) = @_; my($tag, @fields) = split ' ', $line; my %field; for my $field (@fields) { my($key, $val) = split /=/, $field; $field{$key} = $val; } $line =~ s(\s+$)(); my($name) = $line =~ /name=\s*(.*)/; # Die! Die! Die! $field{name} = $name if $name; $tag =~ s/^=/_/; $rc->can($tag) and $rc->$tag(\%field) } sub _ybegin { my($rc, $fields) = @_; my $name = $fields->{name}; $name or return 0; my $entry = $rc->{db}{$name}; if ($entry) { $rc->{current} = $entry; return $entry->ybegin($fields); } $rc->{current} = $rc->{db}{$name} = new Convert::yEnc::Entry $fields; } sub _ypart { my($rc, $fields) = @_; my $entry = $rc->{current}; $entry and $entry->ypart($fields); } sub _yend { my($rc, $fields) = @_; my $entry = $rc->{current}; delete $rc->{current}; $entry and $entry->yend($fields); } sub files { my $rc = shift; my $db = $rc->{db}; keys %$db } sub complete { my($rc, $name) = @_; $name ? $rc->_is_complete($name) : $rc->_complete_files } sub _is_complete { my($rc, $name) = @_; my $entry = $rc->{db}{$name}; $entry and $entry->complete; } sub _complete_files { my $rc = shift; my $db = $rc->{db}; grep { $db->{$_}->complete($_) } keys %$db } sub entry { my($rc, $name) = @_; $rc->{db}{$name} } sub drop { my($rc, $name) = @_; delete $rc->{db}{$name}; } sub save { my $rc = shift; my $file = shift || $rc->{file} or die ref $rc, "::save: no file\n"; open(RC, "> $file.tmp") or die ref $rc, ": Can't open $file.tmp: $!\n"; my $db = $rc->{db}; while (my($name, $entry) = each %$db) { print RC "$name\t$entry\n"; } close RC; rename "$file.tmp", $file or die ref $rc, ": can't rename $file.tmp -> $file: $!\n"; $rc->{file} = $file; } sub _eq { my($a, $b) = @_; my $dba = $a->{db}; my $dbb = $b->{db}; my @a = keys %$dba; my @b = keys %$dbb; @a==@b or return 0; for my $name (@a) { $a->{db}{$name} eq $b->{db}{$name} or return 0; } 1 } 1 __END__ =head1 NAME Convert::yEnc::RC - yEnc file-part database =head1 SYNOPSIS use Convert::yEnc::RC; $rc = new Convert::yEnc::RC; $rc = new Convert::yEnc::RC $file; $ok = $rc->load; $ok = $rc->load($file); $ok = $rc->update ($line); @files = $rc->files; @complete = $rc->complete; $complete = $rc->complete($fileName); $entry = $rc->entry ($fileName); $ok = $rc->drop ($fileName); $rc->save; $rc->save($file); =head1 ABSTRACT yEnc file-part database =head1 DESCRIPTION A C object manages a database of yEnc file parts. Applications pass the C<=ybegin>, C<=ypart>, and C<=yend> lines from yEncoded files to the object, and it keeps track of the files, parts and bytes as they are received. The object reports errors if the sequence of C<=y> lines is inconsistent. Applications can query the object to find out what files, parts, and bytes have been received, and whether a given file is complete. The database can be be saved to and restored from disk. =head2 Database format The database is stored on disk as a flat ASCII file. There is one line in the database for each yEncoded file. A line for a single-part file has 3 fields =over 4 =item * the file name =item * the file size =item * the number of bytes received =back A line for a multi-part file has 4 fields =over 4 =item * the file name =item * the file size =item * a C run list showing which bytes of the file have been recieved =item * a C run list showing which parts of the file have been recieved =back Fields are tab-delimited, so that file names may contain whitespace. Example a.jpg 20000 20000 b.jpg 10000 1-5000 1 =head2 Exports Nothing. =head2 Methods =over 4 =item I<$rc> = C C =item I<$rc> = C C I<$file> Creates and returns a new C object. If I<$file> is supplied, initializes the database from I<$file>. If I<$file> is not supplied, or doesn't exist, initializes the database to empty. =item I<$ok> = I<$rc>->C(I<$file>) Loads the database in I<$file> into I<$rc>. Any existing data in I<$rc> is discarded. Returns true on success. If I<$file> can't be opened, C does nothing and returns false. If I<$file> contains invalid lines, C Cs. When this happens, the state of I<$rc> is undefined. =item $I = I<$rc>->C(I<$line>) Updates I<$rc> according to the contents of I<$line>. I<$line> should be a header (C<=begin>), trailer (C<=end>), or part (C<=part>) line from a yEncoded file. Returns true iff I<$line> is well-formed and consistent with the current state of the database. =item I<@files> = I<$rc>->C Returns a list of all the files in the database. =item I<@complete> = I<$rc>->C Returns a list of all the files in the database that are complete. =item I<$complete> = I<$rc>->C(I<$fileName>) Returns true iff all parts of (I<$fileName>) have been received. =item I<$entry> = I<$rc>->C(I<$fileName>) Returns the database entry for I<$fileName>. I<$entry> is a C object. If I<$fileName> is not in the database, return undef. =item I<$ok> = I<$rc>->C(I<$fileName>) Deletes the entry for I<$fileName> from the database. If I<$fileName> is not in the database, return false. =item I<$rc>->C Writes the contents of I<$newsrc> back to the file from which it was Ced. C Cs if there is an error writing the file. =item I<$newsrc>->C(I<$file>) Writes the contents of I<$newsrc> to I<$file>. Subsequent calls to C() will write to I<$file>. C Cs if there is an error writing the file. =back =head1 BUGS =over 4 item * The database doesn't persist the yEnc 1.2 "total" field to disk. =back =head1 SEE ALSO =over 4 =item * L =item * L =item * http://www.yenc.org =back =head1 AUTHOR Steven W McDougall, Eswmcd@world.std.comE =head1 COPYRIGHT AND LICENSE Copyright (c) 2002-2004 by Steven McDougall. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.