The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

D64::Disk::Layout::Dir - Handling entire Commodore (D64/D71/D81) disk image directories in pure Perl

SYNOPSIS

  use D64::Disk::Layout::Dir;

  # Create an empty disk directory instance:
  my $dir = D64::Disk::Layout::Dir->new();

  # Create a new disk directory instance providing 18 * 256 bytes of scalar data:
  my $dir = D64::Disk::Layout::Dir->new(data => $data);

  # Fetch directory object data as a scalar of 18 * 256 bytes:
  my $data = $dir->data();

  # Replace directory providing 18 * 256 bytes of scalar data:
  $dir->data($data);

  # Fetch directory data as an array of up to 18 * 8 items:
  my @items = $dir->items();

  # Replace directory providing an array of up to 18 * 8 items:
  $dir->items(@items);

  # Get count of non-empty items stored in a disk directory:
  my $num_items = $dir->num_items();

  # Fetch directory data as an array of 18 * sectors:
  my @sectors = $dir->sectors();

  # Replace directory providing an array of 18 * sectors:
  $dir->sectors(@sectors);

  # Fetch an item from a directory listing at any given position:
  my $item = $dir->get(item => $index);

  # Fetch a list of items from a directory listing matching given PETSCII pattern:
  my @items = $dir->get(pattern => $petscii_pattern);

  # Append an item to the end of directory listing, increasing number of files by one element:
  $dir->push(item => $item);

  # Pop and return the last directory item, shortening a directory listing by one element:
  my $item = $dir->pop();

  # Shift the first directory item, shortening a directory listing by one and moving everything down:
  my $item = $dir->shift();

  # Prepend an item to the front of directory listing, and return the new number of elements:
  my $num_items = $dir->unshift(item => $item);

  # Mark directory item designated by an offset as deleted:
  my $num_deleted = $dir->delete(index => $index);

  # Wipe out directory item designated by an offset completely:
  my $num_removed = $dir->remove(index => $index);

  # Add a new directory item to a directory listing:
  my $is_success = $dir->add(item => $item);

  # Put an item to a directory listing at any given position:
  my $is_success = $dir->put(item => $item, index => $index);

  # Print out formatted disk directory listing:
  $dir->print();

DESCRIPTION

D64::Disk::Layout::Dir provides a helper class for D64::Disk::Layout module, enabling users to access and manipulate entire directories of D64/D71/D81 disk images in an object oriented way without the hassle of worrying about the meaning of individual bits and bytes describing each sector data on a disk directory track. The whole family of D64::Disk::Layout modules has been implemented in pure Perl as an alternative to Per Olofsson's "diskimage.c" library originally written in an ANSI C.

METHODS

new

Create an empty disk directory instance:

  my $dir = D64::Disk::Layout::Dir->new();

Create a new disk directory instance providing 18 * 256 bytes of scalar data:

  my $dir = D64::Disk::Layout::Dir->new(data => $data);

Create a new disk directory instance given array with 18 * 256 bytes of data:

  my $dir = D64::Disk::Layout::Dir->new(data => \@data);

Alternatively setup source data structure required to initialize new object using 18 * sector objects:

  my @sectors = (
    # It needs to be a list of D64::Disk::Layout::Sector objects:
    D64::Disk::Layout::Sector->new(data => $sector1, track => 18, sector => 1),
    D64::Disk::Layout::Sector->new(data => $sector2, track => 18, sector => 4),
    D64::Disk::Layout::Sector->new(data => $sector3, track => 18, sector => 7),
    # It needs to contain as many sectors as large directory may be:
    ...
  );

Create a new disk directory instance providing source sector data:

  my $dir = D64::Disk::Layout::Dir->new(sectors => \@sectors);

Directory object may also be initialized using the list of directory item objects:

  my @items = (
    # It needs to be a list of D64::Disk::Dir::Item objects:
    D64::Disk::Dir::Item->new($item1),
    D64::Disk::Dir::Item->new($item2),
    D64::Disk::Dir::Item->new($item3),
    # Up to the maximum number of directory entries (18 * 8 = 144):
    ...
  );

Create a new disk directory instance providing list of dir items:

  my $dir = D64::Disk::Layout::Dir->new(items => \@items);

Individual directory items are stored, accessed and manipulated as D64::Disk::Dir::Item objects.

data

Fetch directory object data as a scalar of 18 * 256 bytes:

  my $data = $dir->data();

Fetch directory object data as an array of 18 * 256 bytes:

  my @data = $dir->data();

Replace directory providing 18 * 256 bytes of scalar data:

  $dir->data($data);

Replace directory given array with 18 * 256 bytes of data:

  $dir->data(@data);
  $dir->data(\@data);

items

Fetch directory object data as an array of up to 18 * 8 items:

  my @items = $dir->items();

This method returns only non-empty directory items.

Replace entire directory providing an array of up to 18 * 8 items:

  $dir->items(@items);
  $dir->items(\@items);

An entire directory object data will be replaced when calling this method. This will happen even when number of items provided as an input parameter is less than the number of non-empty items stored in an object before method was invoked.

num_items

Get count of non-empty items stored in a disk directory:

  my $num_items = $dir->num_items();

sectors

Fetch directory object data as an array of 18 * sector objects:

  my @sectors = $dir->sectors();

Replace entire directory providing an array of 18 * sector objects:

  $dir->sectors(@sectors);
  $dir->sectors(\@sectors);

num_sectors

Get total number of allocated sectors that can be used to store disk directory data:

  my $num_sectors = $dir->num_sectors(count => 'all');

In the case of a D64 disk image format, the value of 18 is always returned, as this is a standard number of sectors designated to store disk directory data.

Get number of currently used sectors that are used to store actual disk directory data:

  my $num_sectors = $dir->num_sectors(count => 'used');

In this case method call returns an integer value between 0 and 18 (total count of sectors used to store actual data), i.a. for an empty disk directory 0 is returned, and for a disk directory filled with more than 136 files the value of 18 will be retrieved.

count parameter defaults to all.

get

Fetch an item from a directory listing at any given position:

  my $item = $dir->get(index => $index);

$index indicates an offset from the beginning of a directory listing, with count starting from 0. When $index indicates an element beyond the number of non-empty items stored in a disk directory, an undefined value will be returned.

Fetch a list of items from a directory listing matching given PETSCII pattern:

  use Text::Convert::PETSCII qw(:convert);

  my $pattern = ascii_to_petscii 'workstage*';

  my @items = $dir->get(pattern => $pattern);

pattern is expected to be any valid PETSCII text string. Such call to this method always returns all items with filename matching given PETSCII pattern.

push

Append an item to the end of directory listing, increasing number of files by one element:

  $dir->push(item => $item);

$item is expected to be a valid D64::Disk::Dir::Item object. This method will not work when number of non-empty items stored in a disk directory has already reached its maximum.

pop

Pop and return the last non-empty directory item, shortening a directory listing by one element:

  my $item = $dir->pop();

When there is at least one non-empty item stored in a disk directory, a D64::Disk::Dir::Item object will be returned. Otherwise return value is undefined.

shift

Shift the first directory item, shortening a directory listing by one and moving everything down:

  my $item = $dir->shift();

When there is at least one non-empty item stored in a disk directory, a D64::Disk::Dir::Item object will be returned. Otherwise return value is undefined.

unshift

Prepend an item to the front of directory listing, and return the new number of elements:

  my $num_items = $dir->unshift(item => $item);

$item is expected to be a valid D64::Disk::Dir::Item object. This method will not work when number of non-empty items stored in a disk directory has already reached its maximum.

delete

Mark directory item designated by an offset as deleted:

  my $num_deleted = $dir->delete(index => $index);

Mark directory item being the first one to match given PETSCII pattern as deleted:

  use Text::Convert::PETSCII qw(:convert);

  my $pattern = ascii_to_petscii 'workstage*';

  my $num_deleted = $dir->delete(pattern => $pattern, global => 0);

Mark all directory items matching given PETSCII pattern as deleted:

  use Text::Convert::PETSCII qw(:convert);

  my $pattern = ascii_to_petscii 'workstage*';

  my $num_deleted = $dir->delete(pattern => $pattern, global => 1);

pattern is expected to be any valid PETSCII text string. global parameter defaults to 0, hence deleting only a single file matching given criteria by default. When set to any true value, it will trigger deletion of all items with filename matching given PETSCII pattern.

A call to this method always returns the number of successfully deleted items. When deleting an item designated by an offset of an already deleted directory item, such operation does not contribute to the count of successfully deleted items during such a particular method call. In other words, delete an item once, and you get it counted as a successfully deleted one, delete the same item again, and it will not be counted as a deleted one anymore. Of course an item remains delete in a directory listing, it just does not contribute to a value that is returned from this method's call.

Note that this method does not remove an entry from directory layout, it only marks it as deleted. In order to wipe out an entry entirely, see description of "remove" method.

remove

Wipe out directory item designated by an offset entirely:

  my $num_removed = $dir->remove(index => $index);

Wipe out directory item being the first one to match given PETSCII pattern entirely:

  use Text::Convert::PETSCII qw(:convert);

  my $pattern = ascii_to_petscii 'workstage*';

  my $num_removed = $dir->remove(pattern => $pattern, global => 0);

Wipe out all directory items matching given PETSCII pattern entirely:

  use Text::Convert::PETSCII qw(:convert);

  my $pattern = ascii_to_petscii 'workstage*';

  my $num_removed = $dir->remove(pattern => $pattern, global => 1);

pattern is expected to be any valid PETSCII text string. global parameter defaults to 0, hence removing only a single file matching given criteria by default. When set to any true value, it will trigger removal of all items with filename matching given PETSCII pattern.

A call to this method always returns the number of successfully removed items.

Note that this method removes an item from directory layout completely. It works a little bit like splice, Perl's core method, removing a single element designated by an offset from an array of disk directory items, however it does not replace it with any new elements, it just shifts the remaining items, shortening a directory listing by one and moving everything from a given offset down. In order to safely mark given file as deleted without removing it from a directory listing, see description of "delete" method.

add

Add a new directory item to a directory listing:

  my $is_success = $dir->add(item => $item);

Add a new directory item designated by an offset:

  my $is_success = $dir->add(item => $item, index => $index);

$item is expected to be a valid D64::Disk::Dir::Item object.

A call to this method returns true on a successful addition of a new entry, and false otherwise. Addition of a new item may not be possible, for instance when a maximum number of allowed disk directory elements has already been reached.

$index indicates an offset from the beginning of a directory listing where a new item should be added, with count starting from 0. Note that this method will not only insert a new item into a disk directory, it will also shift the remaining items, extending a directory listing by one and moving everything from a given offset up. When $index indicates an element beyond the number of non-empty items currently stored in a disk directory, subroutine will fail and an undefined value will be returned, because such operation would not make much sense (such added entry would not be obtainable from a directory listing anyway). It will also not work when number of non-empty items stored in a disk directory has already reached its maximum. Please note that this operation will not replace a "*", or "splat" file it encounters at a given offset, rather it will always it altogether with the remaining items, unlike add method called without an index parameter specified at all, which is described in the next paragraph.

When $index parameter is unspecified, the method behaves as follows. It finds the first empty slot in a directory listing (that is a first directory item with a "closed" flag unset), and writes given item at that exact position. It will however not work when there is no writable slot in a directory listing available at all. Please note that this operation may or may not write given item at the end of a directory listing, since it will replace any "*", or "splat" file it encounters earlier on its way. In most cases this is a desired behaviour, that is why it is always performed as a default action.

put

Put an item to a directory listing at any given position:

  my $is_success = $dir->put(item => $item, index => $index);

$item is expected to be a valid D64::Disk::Dir::Item object. A call to this method returns true on a successful put of a new entry, and false otherwise.

$index is a required parameter that indicates an offset from the beginning of a directory listing where a new item should be put, with count starting from 0. Note that this method does not just insert a new item into a disk directory, it rather replaces an existing item previously stored at a given offset. When $index indicates an element beyond the number of non-empty items currently stored in a disk directory, subroutine will fail and an undefined value will be returned, because such operation would not make much sense (such added entry would not be obtainable from a directory listing anyway).

print

Print out formatted disk directory listing:

  $dir->print(fh => $fh, as_petscii => $as_petscii);

$fh defaults to the standard output. as_petscii defaults to false (meaning that ASCII characters will be printed out by default).

A printout does not include header and number of blocks free lines, because information about disk title, disk ID and number of free sectors is stored in a Block Availability Map (see D64::Disk::BAM for more details on how to access these bits of information).

BUGS

There are no known bugs at the moment. Please report any bugs or feature requests.

EXPORT

None. No method is exported into the caller's namespace neither by default nor explicitly.

SEE ALSO

D64::Disk::BAM, D64::Disk::Dir::Item, D64::Disk::Image, D64::Disk::Layout, D64::Disk::Layout::Sector, D64::Disk::Status.

AUTHOR

Pawel Krol, <pawelkrol@cpan.org>.

VERSION

Version 0.06 (2021-01-18)

COPYRIGHT AND LICENSE

Copyright 2013-2021 by Pawel Krol <pawelkrol@cpan.org>.

This library is free open source software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.6 or, at your option, any later version of Perl 5 you may have available.

PLEASE NOTE THAT IT COMES WITHOUT A WARRANTY OF ANY KIND!