home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Acorn User 10
/
AU_CD10.iso
/
Updates
/
Perl
/
Non-RPC
/
!Perl
/
riscos
/
RISCOS
/
ALF.pm
next >
Wrap
Text File
|
1998-07-31
|
10KB
|
380 lines
package RISCOS::ALF::Time;
use strict;
use vars qw ($VERSION);
$VERSION = 0.01;
sub new ($$) {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
my ($data) = @_;
return undef unless defined ($data) && (length $data == 8);
my ($t1, $micro, $t2) = unpack 'a4Sa2', $data;
# erk. Little endian short
$self->{__TIME} = $t2 . $t1;
$self->{__MICRO} = $micro if $micro; # Don't store it if it is zero
bless ($self, $class);
}
sub Time {
my $self = shift;
$self->{__TIME};
}
sub Micro {
my $self = shift;
$self->{__MICRO} ||= 0;
}
sub TimeValid {
my $self = shift;
return 0 unless $self->{__TIME} =~ /[:-ÿ]\0$/s; # 1980 to 2238
defined $self->{__MICRO} ? 1 : 2; # Most valid if microseconds == 0
}
package RISCOS::ALF::DirEntry;
use Carp;
use strict;
use vars qw (@ISA $VERSION);
$VERSION = 0.01;
@ISA = qw();
# Pass a ref to scalar data, and an offset, or real data
sub new ($$$;$$) {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = {};
my ($chunkdata, $chunkfile, $length, $offset) = @_;
return undef unless defined $chunkdata;
my $what = 'chunk';
if (ref $chunkdata eq 'SCALAR' or ref $chunkdata eq 'LVALUE') {
# Hmm, passed a reference into something bigger
if ($offset + 16 >= length $$chunkdata) {
return wantarray ? () : undef; # OK, maybe not :-)
}
$what = 'reference';
$length = unpack 'V', substr $$chunkdata, $offset + 4, 4;
$chunkdata = substr $$chunkdata, $offset, $length;
}
if ($^W and defined ($length) and length $chunkdata != $length) {
carp "${class}::new passed $what with length reported as " . $length
. ", actualy " . length $chunkdata;
}
$length = length $chunkdata;
carp "$class length $length is not a multiple of 4" if $^W and $length & 3;
if ($length < 16) {
carp "$class length $length < 16" if $^W;
return wantarray ? () : undef;
}
my ($chunkindex, $entrylen, $datalen, $data) = unpack 'V3a*', $chunkdata;
if ($entrylen != $length) {
carp "$class entry reports length as $entrylen, data supplied is "
. $length if $^W;
return wantarray ? () : undef;
}
carp "$class data length of $datalen is too great for total length $length"
if ($^W && $datalen > ($entrylen - 12));
return undef unless defined (my ($name) = $data =~ /^([^\0]*)/s);
$self->{__INDEX} = $chunkindex;
$self->{__NAME} = $name;
$self->{__CHUNK} = $chunkfile->By_Number ($chunkindex);
my $time = new RISCOS::ALF::Time substr ($data, (length ($name) + 4) & ~3);
$self->{__TIME} = $time if defined $time;
return bless ($self, $class) unless wantarray;
(bless ($self, $class), $length, $name)
}
sub Name {
my $self = shift;
$self->{'__NAME'};
}
sub Index {
my $self = shift;
$self->{'__INDEX'};
}
sub Time {
my $self = shift;
$self->{'__TIME'};
}
sub Chunk ($;$) {
my $self = shift;
my $chunk = $self->{'__CHUNK'};
$self->{'__CHUNK'} = $_[0] if defined $_[0];
$chunk;
}
package RISCOS::ALF;
require RISCOS::Chunkfile;
use Carp;
use strict;
use vars qw (@ISA $VERSION);
$VERSION = 0.02;
@ISA = 'RISCOS::Chunkfile';
sub new ($$) {
my $proto = shift;
my $class = ref($proto) || $proto;
my $self = $class->SUPER::new ($_[0]);
foreach my $what (qw(LIB_DIRY LIB_TIME LIB_VSRN OFL_SYMT OFL_TIME)) {
if ($self->Multiple ($what)) {
carp "AOF file has '$what' chunks at positions "
. join (' ', @{$self->Lookup ($what)})
if $^W;
return undef;
}
}
my $chunk = $self->Chunk ('LIB_VRSN');
if (defined $chunk) {
my $version = unpack 'I', $chunk->Data();
unless (4 == $chunk->Length) {
carp 'LIB_VRSN length ' . $chunk->Length() . ' != 4' if $^W;
return undef;
}
$self->{'__VERSION'} = $version;
}
unless (defined ($chunk = $self->Chunk ('LIB_DIRY'))) {
carp "ALF file has no 'LIB_DIRY'" if $^W;
return undef;
}
$chunk = \$chunk->Data; # Ref to scalar data
my $entries = {};
my ($position, $entry, $length, $name) = (0);
while ((($entry, $length, $name)
= new RISCOS::ALF::DirEntry $chunk, $self, undef, $position),
defined $entry) {
$entries->{$name} = $entry;
$position += $length
}
$self->{'__DIR'} = $entries;
if (defined ($chunk = $self->Chunk ('LIB_TIME'))) {
my $time = new RISCOS::ALF::Time $chunk->Data;
$self->{'__TIME'} = $time if defined $time;
}
if (defined ($chunk = $self->Chunk ('OFL_TIME'))) {
my $time = new RISCOS::ALF::Time $chunk->Data;
$self->{'__SYM_TIME'} = $time if defined $time;
}
$self;
}
sub Dir ($) {
my $self = shift;
$self->{'__DIR'};
}
sub Dir_Lookup ($$) {
my $self = shift;
$self->{'__DIR'}->{$_[0]};
}
sub Time ($) {
my $self = shift;
$self->{'__TIME'};
}
sub Sym_Time ($) {
my $self = shift;
$self->{'__TIME'};
}
sub Version ($) {
my $self = shift;
$self->{'__VERSION'};
}
1;
__END__
=head1 NAME
RISCOS::ALF -- manipulate Acorn Library Format files
=head1 SYNOPSIS
use RISCOS::ALF;
$library = new RISCOS::ALF $file;
print "Library file '$file':\n";
print ' ALF version ', $library->Version, "\n";
=head1 DESCRIPTION
C<RISCOS::ALF> provides a class derived from C<RISCOS::Chunkfile> to manipulate
the contents of B<A>corn B<L>ibrary B<F>ormat files. It provides a classes
C<RISCOS::ALF::Time> to manipulate 8 byte C<ALF> microsecond timestamps and
C<RISCOS::ALF::DirEntry> to store details of each directory entry in an C<ALF>
file.
Currently the implementation does not decode the library's symbol table.
=head2 Methods
=over 4
=item new <array_ref>
=item new <file>
If passed a reference to an array it is assumed to be an array of
C<RISCOS::Chunk> objects to use as the file contents. Otherwise calls
C<RISCOS::File::load> to loads the file specified using and checks that it is
C<ALF>. Hence I<file> can be a filename, a reference to a filehandle, or a
reference to a scalar which is used as the file's contents.
If passed an array reference then this is used internally in the object, so
should be created with the anonymous array constructor C<[]> rather than a
reference to a named array variable (see L<perldsc/Common Mistakes>).
Returns undefined if there was an error, or the file contents are corrupt.
=item Dir
Returns a reference to the hash of C<RISCOS::ALF::DirEntry> objects describing
the C<ALF> file's directory. The keys are member names, the values the objects.
=item Dir_Lookup <name>
Returns the C<RISCOS::ALF::DirEntry> object for I<name> in the C<ALF> file's
directory.
=item Time
Returns the C<RISCOS::ALF::Time> object that gives the library timestamp - the
time when the library was last modified.
=item Sym_Time
Returns the C<RISCOS::ALF::Time> object that gives the library symbol table
timestamp (or undefined if there is no symbol table) - the time when the symbol
table was last modified.
=item Version
Returns the version number of the C<ALF> library. The current version is 1.
=back
=head1 RISCOS::ALF::Time
The C<RISCOS::ALF::Time> class is used to hold 8 byte microsecond timestamps as
used in C<ALF> libraries. It provides the following methods:
=over 4
=item new <packed_timestamp>
Creates a new C<RISCOS::ALF::Time> object from the 8 byte packed timestamp in an
C<ALF> library.
=item Time
Returns the B<6> byte timestamp as centiseconds from the start of 1900. Assuming
little endian byte order the last character should be C<"\0"> - chop this to get
a standard B<5> byte RISCOS timestamp. Note that C<RISCOS::Time> functions will
perform this conversion automatically.
=item Micro
Returns the microseconds field of the timestamp. This is usually 0.
=item TimeValid
Performs a sanity check on the time stored in the object. Returns:
=over 4
=item 0 if the date is outside the range (roughly) 1980 - 2238
=item 1 if the date is in this range but the microseconds field is non-zero
=item 2 if the date is in this range and the microseconds field is zero
=back
=back
=head1 RISCOS::ALF::DirEntry
The C<RISCOS::ALF::DirEntry> class is used to hold details of directory entries
in C<ALF> libraries. It provides the following methods:
=over 4
=item new <scalar_reference>, <chunkfile>, undef, <offset>
=item new <file_data>, <chunkfile> ,<length>]
Creates a new C<RISCOS::ALF::DirEntry> from the supplied data. If passed a
scalar this will be treated as being a single 'C<DATA>' item from a
'C<LIB_DIRY>' chunk in an C<ALF> file. I<length>, if defined, is used to verify
the length recorded in the 'C<DATA>' item.
If passed a reference to a scalar, then this is dereferenced and the I<offset>
parameter used to locate the 'C<DATA>' within this scalar. This allows an
entire 'C<LIB_DIRY>' chunk to be converted with minimal string copying.
(Remember that the length of a 'C<DATA>' item is unknown until decoding
commences, so it is not possible to pass a substring to C<new> without I<a
priori> knowledge of the length.)
I<chunkfile> is the C<RISCOS::Chunkfile> object that contains the chunk that
this directory entry referes to.
In scalar context returns the object. In B<array context> returns
C<(object, length, name)>. I<length> is added to the current I<offset> to
calculate the offset of the next 'C<DATA>' item.
=item Name
Returns the name of this object, usually the file name whose contents form the
data for this library member.
=item Index
Returns the index of the chunk which holds the data for this member.
=item Time
Returns the C<RISCOS::ALF::Time> object that gives the timestamp for this
member. This is the last-modified time of the file whose contents form the data
for this library member.
=item Chunk
Returns the C<RISCOS::Chunk> object for this entry.
=back
=head1 BUGS
As noted, methods to manipulate the library symbol table are currently
unimplemented. Wildcard matching on library members is not implemented.
=head1 AUTHOR
Nicholas Clark <F<nick@unfortu.net>>