#!/usr/local/bin/perl

=head1 NAME

  applesingle - extract the data fork from an AppleSingle byte stream

=head1 DESCRIPTION

This script reads a byte stream in AppleSingle format, extracts the header
information, and then reads the entry ids to determine the location of the
data fork.  It writes the data fork to a file called filename.data.

The AppleSingle stream is described in RFC 1740.

=head1 AUTHOR

  Monte Mitzelfelt <monte-applesingle@gonefishing.org>

=pod SCRIPT CATEGORIES

File/Format Conversion

=cut

@eids = ( undef, 'Data Fork', 'Resource Fork', 'Real Name', 'Comment',
          'Icon, B&W', 'Icon, Colour', 'File Dates Info', 'Finder Info',
          'Macintosh File Info', 'ProDOS File Info', 'MS-DOS File Info',
          'Short Name', 'AFP File Info', 'Directory ID', ) ;
map { $maxlabel = length if $maxlabel < length } @eids ;

foreach $file ( @ARGV ) {
  open FILE, "< $file" ;
  $length = 26 ;
  read FILE, $_, $length ;
  ( $magic, $vers, $fill, $num, ) = unpack( "N N a16 n", $_ ) ;
  unless ( $magic == 0x00051600 ) {
    printf STDERR "$file is not an AppleSingle file (magic number = %08x)\n",
                   $magic ;
    close FILE ;
    next ;
  }
  $fill =~ tr/\c@/@/ ;
  printf "0x%08x 0x%08x %s %d\n", $magic, $vers, $fill, $num, ;
  for ( $i = 0 ; $i < $num ; $i++ ) {
    read FILE, $_, 12 ;
    ( $eid, $off, $len ) = unpack( "N3", $_ ) ;
    printf "\t%${maxlabel}s %7d %7d\n", $eids[$eid], $off, $len ;
    if ( $eid ) {
      $dataoff = $off ;
      $datalen = $len ;
    }
  }
  seek FILE, $dataoff, 0  or die "can't seek: $!" ;
  open NEWFILE, ">$file.data" or die "Can't open $file.data: $!" ;
  $total   = 0 ;
  $blksize = 4096 ;
  while ( $read = read FILE, $_, $blksize ) {
    $total += $read ;
    print "$blksize\n" ;
    print NEWFILE $_ ;
    $blksize = $datalen - $total if $datalen - $total < $blksize ;
  }
  close NEWFILE ;
  close FILE ;
}