home *** CD-ROM | disk | FTP | other *** search
- 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>>
-