package Convert::yEnc::Decoder; use strict; use IO::File; use warnings; use constant yEncBrainDamage => 1; sub new { my($class, $dir) = @_; my $decoder = { dir => $dir || '.' }; bless $decoder, $class } sub out_dir { my($decoder, $dir) = @_; $decoder->{dir} = $dir; } sub decode { my($decoder, $in) = @_; delete $decoder->{temp}; $decoder->_in($in); $decoder->_begin; $decoder->_out; $decoder->_part if $decoder->{temp}{begin}{part}; $decoder->_body; $decoder->_end; } sub _in { my($decoder, $in) = @_; my $IN = ref $in ? $in : defined $in ? (new IO::File $in) : \*STDIN; $IN or die ref $decoder, "::decode: Can't open $in: $!\n"; $decoder->{temp}{IN} = $IN; } sub _begin { my $decoder = shift; my $temp = $decoder->{temp}; my $IN = $temp->{IN}; my $begin; while ($begin = <$IN>) { $begin =~ /^=ybegin/ and last; } $begin or die ref $decoder, "::_begin: Can't find =ybegin line\n"; my @begin = split ' ', $begin; for my $field (@begin) { my($key, $val) = split /=/, $field; $temp->{begin}{$key} = $val; } my($name) = $begin =~ /name=(.*)/; # Horrid Martian. $name =~ s/^\s+|\s+$//g; $temp->{begin}{name} = $name; $temp->{line}{ybegin} = $begin; } sub _out { my $decoder = shift; my $dir = $decoder->{dir}; my $temp = $decoder->{temp}; my $name = $temp->{begin}{name}; my $file = "$dir/$name"; my $mode = O_CREAT | O_WRONLY; my $OUT = new IO::File $file, $mode or die ref $decoder, "::_out: Can't open $file: $!\n"; binmode $OUT or die ref $decoder, "::_out: Can't binmode $file: $!\n"; $temp->{name} = $name; $temp->{file} = $file; $temp->{OUT } = $OUT; } sub _part { my $decoder = shift; my $temp = $decoder->{temp}; my $IN = $temp->{IN }; my $OUT = $temp->{OUT}; my $part = <$IN>; $part =~ /^=ypart/ or die ref $decoder, "::_part: No =ypart line\n"; my @part = split ' ', $part; for my $field (@part) { my($key, $val) = split /=/, $field; $temp->{part}{$key} = $val; } my $begin = $temp->{part}{begin}; my $end = $temp->{part}{end }; my $offset = $begin - yEncBrainDamage; my $size = $end - $offset; seek $OUT, $offset, 0 or die ref $decoder, "::_part: Can't seek to $begin: $!\n"; $temp->{part}{size } = $size; $temp->{line}{ypart} = $part; } sub _body { my $decoder = shift; my $temp = $decoder->{temp}; my $file = $temp->{file}; my $IN = $temp->{IN}; my $OUT = $temp->{OUT}; my $line; while ($line = <$IN>) { $line =~ /^=yend/ and last; chomp $line; $decoder->_line($line); print $OUT $line or die "can't print to $file: $!\n"; } close $OUT; $temp->{line}{yend} = $line; } sub _line { $_[1] =~ s/=(.)/chr(ord($1)+256-64 & 255)/egosx; $_[1] =~ tr[\000-\377][\326-\377\000-\325]; } sub _end { my $decoder = shift; my $temp = $decoder->{temp}; my $end = $temp->{line}{yend}; $end =~ /^=yend/ or die ref $decoder, "::end: No =yend line\n"; my @end = split ' ', $end; for my $field (@end) { my($key, $val) = split /=/, $field; $temp->{end}{$key} = $val; } my $beginSize = $temp->{begin}{size}; my $partSize = $temp->{part }{size}; my $endSize = $temp->{end }{size}; my $decodeSize = defined $partSize ? $partSize : $beginSize; $decodeSize == $endSize or die ref $decoder, "::_end: Begin/PartSize $decodeSize != EndSize $endSize\n"; if (not defined $partSize) { my $file = $temp->{file}; my $fileSize = (stat $file)[7]; $beginSize == $fileSize or die ref $decoder, "::_end: BeginSize $beginSize != FileSize $fileSize\n"; } $temp->{size} = $decodeSize; } sub name { shift->{temp}{name} } sub file { shift->{temp}{file} } sub size { shift->{temp}{size} } sub ybegin { shift->{temp}{line}{ybegin} } sub ypart { shift->{temp}{line}{ypart } } sub yend { shift->{temp}{line}{yend } } 1 __END__ =head1 NAME Convert::yEnc::Decoder - decodes yEncoded files =head1 SYNOPSIS use Convert::yEnc::Decoder; $decoder = new Convert::yEnc::Decoder; $decoder = new Convert::yEnc::Decoder $dir; $decoder->out_dir($dir); eval { $decoder->decode( $file); $decoder->decode(\*FILE); $decoder->decode; }; print $@ if $@; $name = $decoder->name; $file = $decoder->file; $size = $decoder->size; $ybegin = $decoder->ybegin; $ypart = $decoder->ypart; $yend = $decoder->yend; =head1 ABSTRACT yEnc decoder =head1 DESCRIPTION C decodes a yEncoded file and writes it to disk. Methods are provided for returning information about the decoded file. =head2 Exports Nothing. =head2 Methods =over 4 =item I<$decoder> = C C =item I<$decoder> = C C I<$dir> Creates and returns a new C object. Decoded files will be written to I<$dir>. If I<$dir> is omitted, it defaults to the current working directory. =item I<$decoder>->C(I<$dir>) Sets the output directory to I<$dir>. =item I<$decoder>->C(I<$file>) =item I<$decoder>->C(I<\*FILE>) =item I<$decoder>->C Decodes a file. Cs if there are any errors. The first form reads from the file named I<$file>. The second form reads from the file handle I. The third form reads from C. The data stream need not begin at the C<=yBegin> line; C will search until it finds it. C stops reading when it finds the C<=yend> line, so C can decode multiple files from the same data stream. C may be called repeatedly on the same C object to decode multiple files. =item I<$name> = I<$decoder>->C After a successful decode, returns the name of the file that was created. =item I<$file> = I<$decoder>->C After a successful decode, returns the complete path of the file that was created. =item I<$size> = I<$decoder>->C After a successful decode, returns the size of the decoded file. =item I<$ybegin> = I<$decoder>->C After a successful decode, returns the C<=ybegin> line. =item I<$ypart> = I<$decoder>->C After a successful decode, returns the C<=ypart> line, or undef if there wasn't one. =item I<$yend> = I<$decoder>->C After a successful decode, returns the C<=yend> line. =back =head1 NOTES =head2 1-liner To decode a single file on the command line, write perl -MConvert::yEnc::Decoder -e 'Convert::yEnc::Decoder->new->decode' < myFile =head1 TODO =over 4 =item * CRCs =back =head1 SEE ALSO =over 4 =item * L =item * L =item * L =back =head1 AUTHOR Steven W McDougall, Eswmcd@world.std.comE =head1 COPYRIGHT AND LICENSE Copyright (c) 2002-2008 by Steven McDougall. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.