Palm-PDB-1.400/0000755000175000017500000000000012476641116011126 5ustar cjmcjmPalm-PDB-1.400/xt/0000755000175000017500000000000012476641116011561 5ustar cjmcjmPalm-PDB-1.400/xt/release/0000755000175000017500000000000012476641116013201 5ustar cjmcjmPalm-PDB-1.400/xt/release/pod-coverage.t0000644000175000017500000000033412476641116015741 0ustar cjmcjm#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodCoverageTests. use Test::Pod::Coverage 1.08; use Pod::Coverage::TrustPod; all_pod_coverage_ok({ coverage_class => 'Pod::Coverage::TrustPod' }); Palm-PDB-1.400/xt/release/pod-syntax.t0000644000175000017500000000022012476641116015466 0ustar cjmcjm#!perl # This file was automatically generated by Dist::Zilla::Plugin::PodSyntaxTests. use Test::More; use Test::Pod 1.41; all_pod_files_ok(); Palm-PDB-1.400/lib/0000755000175000017500000000000012476641116011674 5ustar cjmcjmPalm-PDB-1.400/lib/Palm/0000755000175000017500000000000012476641116012565 5ustar cjmcjmPalm-PDB-1.400/lib/Palm/PDB.pm0000644000175000017500000014145012476641116013535 0ustar cjmcjmpackage Palm::PDB; # # Perl module for reading and writing Palm databases (both PDB and PRC). # # Copyright (C) 1999, 2000, Andrew Arensburger. # # This program is free software; you can redistribute it and/or modify # it under the same terms as Perl itself. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either the # GNU General Public License or the Artistic License for more details. # # A Palm database file (either .pdb or .prc) has the following overall # structure: # Header # Index header # Record/resource index # Two NUL(?) bytes # Optional AppInfo block # Optional sort block # Records/resources # See http://www.palmos.com/dev/tech/docs/fileformats.zip # for details. use 5.006; use strict; our $VERSION = '1.400'; # This file is part of Palm-PDB 1.400 (March 7, 2015) # ABSTRACT: Parse Palm database files use constant 1.03 { # accepts hash reference dmRecordIDReservedRange => 1, # The range of upper bits in the database's # uniqueIDSeed from 0 to this number are # reserved and not randomly picked when a #database is created. EPOCH_1904 => 2082844800, # Difference between Palm's # epoch (Jan. 1, 1904) and # Unix's epoch (Jan. 1, 1970), # in seconds. HeaderLen => 32+2+2+(9*4), # Size of database header RecIndexHeaderLen => 6, # Size of record index header IndexRecLen => 8, # Length of record index entry IndexRsrcLen => 10, # Length of resource index entry }; our %PDBHandlers = (); # Record handler map our %PRCHandlers = (); # Resource handler map sub new { my $class = shift; my $params = shift; my $self = {}; # Initialize the PDB. These values are just defaults, of course. $self->{'name'} = $params->{'name'} || ""; $self->{'attributes'} = $params->{'attributes'} || {}; $self->{'version'} = $params->{'version'} || 0; my $now = time; $self->{'ctime'} = $params->{'ctime'} || $now; $self->{'mtime'} = $params->{'mtime'} || $now; $self->{'baktime'} = $params->{'baktime'} || -(EPOCH_1904); $self->{'modnum'} = $params->{'modnum'} || 0; $self->{'type'} = $params->{'type'} || "\0\0\0\0"; $self->{'creator'} = $params->{'creator'} || "\0\0\0\0"; $self->{'uniqueIDseed'} = $params->{'uniqueIDseed'} || 0; $self->{"2NULs"} = "\0\0"; # This will be set when any elements of the object are changed $self->{'dirty'} = 0; # Calculate a proper uniqueIDseed if the user has not provided # a correct one. if ($self->{'uniqueIDseed'} <= ((dmRecordIDReservedRange + 1) << 12)) { my $uniqueIDseed = 0; do { $uniqueIDseed = int(rand(0x0FFF)); } while ($uniqueIDseed <= dmRecordIDReservedRange); $self->{'uniqueIDseed'} = $uniqueIDseed << 12; $self->{'uniqueIDseed'} &= 0x00FFF000; # Isolate the upper 12 seed bits. } bless $self, $class; return $self; } #' <-- For Emacs. sub RegisterPDBHandlers { my $handler = shift; # Name of class that'll handle # these databases my @types = @_; my $item; foreach $item (@types) { if (ref($item) eq "ARRAY") { $PDBHandlers{$item->[0]}{$item->[1]} = $handler; } else { $PDBHandlers{$item}{""} = $handler; } } } sub RegisterPRCHandlers { my $handler = shift; # Name of class that'll handle # these databases my @types = @_; my $item; foreach $item (@types) { if (ref($item) eq "ARRAY") { $PRCHandlers{$item->[0]}{$item->[1]} = $handler; } else { $PRCHandlers{$item}{""} = $handler; } } } #' # _open sub _open { my($self, $mode, $fname) = @_; my $handle; if (ref($fname)) { # Already a filehandle if (ref($fname) eq 'GLOB' or UNIVERSAL::isa($fname,"IO::Seekable")) { $handle = $fname; } # Probably a reference to a SCALAR else { unless (eval 'open $handle, $mode, $fname') { if ($@ ne '') { die "Open of \"$fname\" unsupported: $@\n"; } else { die "Can't open \"$fname\": $!\n"; } } } } else { # Before 5.6.0 "autovivified file handles" don't exist eval 'use IO::File; $handle = new IO::File' if $] < 5.006; open $handle, "$mode $fname" or die "Can't open \"$fname\": $!\n"; } return $handle; } # Load sub Load { my $self = shift; my $fname = shift; # Filename to read from my $buf; # Buffer into which to read stuff my $handle = $self->_open('<', $fname); return undef unless defined $handle; binmode $handle; # Read as binary file under MS-DOS # Get the size of the file. It'll be useful later seek $handle, 0, 2; # 2 == SEEK_END. Seek to the end. $self->{_size} = tell $handle; seek $handle, 0, 0; # 0 == SEEK_START. Rewind to the beginning. # Read header my $name; my $attributes; my $version; my $ctime; my $mtime; my $baktime; my $modnum; my $appinfo_offset; my $sort_offset; my $type; my $creator; my $uniqueIDseed; read $handle, $buf, HeaderLen; # Read the PDB header # Split header into its component fields ($name, $attributes, $version, $ctime, $mtime, $baktime, $modnum, $appinfo_offset, $sort_offset, $type, $creator, $uniqueIDseed) = unpack "a32 n n N N N N N N a4 a4 N", $buf; # database names must include a terminating NUL. die "bogus database name! is this really a PalmOS file?" unless $name =~ /.+\0/; ($self->{name} = $name) =~ s/\0.*$//; $self->{attributes}{resource} = 1 if $attributes & 0x0001; $self->{attributes}{"read-only"} = 1 if $attributes & 0x0002; $self->{attributes}{"AppInfo dirty"} = 1 if $attributes & 0x0004; $self->{attributes}{backup} = 1 if $attributes & 0x0008; $self->{attributes}{"OK newer"} = 1 if $attributes & 0x0010; $self->{attributes}{reset} = 1 if $attributes & 0x0020; $self->{attributes}{open} = 1 if $attributes & 0x8000; $self->{attributes}{launchable} = 1 if $attributes & 0x0200; # Attribute names as of PalmOS 5.0 ( see /Core/System/DataMgr.h ) $self->{'attributes'}{'ResDB'} = 1 if $attributes & 0x0001; $self->{'attributes'}{'ReadOnly'} = 1 if $attributes & 0x0002; $self->{'attributes'}{'AppInfoDirty'} = 1 if $attributes & 0x0004; $self->{'attributes'}{'Backup'} = 1 if $attributes & 0x0008; $self->{'attributes'}{'OKToInstallNewer'} = 1 if $attributes & 0x0010; $self->{'attributes'}{'ResetAfterInstall'} = 1 if $attributes & 0x0020; $self->{'attributes'}{'CopyPrevention'} = 1 if $attributes & 0x0040; $self->{'attributes'}{'Stream'} = 1 if $attributes & 0x0080; $self->{'attributes'}{'Hidden'} = 1 if $attributes & 0x0100; $self->{'attributes'}{'LaunchableData'} = 1 if $attributes & 0x0200; $self->{'attributes'}{'Recyclable'} = 1 if $attributes & 0x0400; $self->{'attributes'}{'Bundle'} = 1 if $attributes & 0x0800; $self->{'attributes'}{'Open'} = 1 if $attributes & 0x8000; $self->{version} = $version; $self->{ctime} = $ctime - EPOCH_1904; $self->{mtime} = $mtime - EPOCH_1904; $self->{baktime} = $baktime - EPOCH_1904; $self->{modnum} = $modnum; # _appinfo_offset and _sort_offset are private fields $self->{_appinfo_offset} = $appinfo_offset; $self->{_sort_offset} = $sort_offset; $self->{type} = $type; $self->{creator} = $creator; $self->{uniqueIDseed} = $uniqueIDseed; # XXX strictly speaking, ctime/mtime/baktime values before 1990 are quite # unlikely. Palm was founded in 1992, so even allowing for some prototypes. # This is another way one could detect bogus databases. if( $self->{_appinfo_offset} > $self->{_size} ) { die "AppInfo block offset beyond end of file!"; } if( $self->{_sort_offset} > $self->{_size} ) { die "Sort block offset beyond end of file!"; } # Rebless this PDB object, depending on its type and/or # creator. This allows us to magically invoke the proper # &Parse*() function on the various parts of the database. # Look for most specific handlers first, least specific ones # last. That is, first look for a handler that deals # specifically with this database's creator and type, then for # one that deals with this database's creator and any type, # and finally for one that deals with anything. my $handler; if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) { # Look among resource handlers $handler = $PRCHandlers{$self->{creator}}{$self->{type}} || $PRCHandlers{undef}{$self->{type}} || $PRCHandlers{$self->{creator}}{""} || $PRCHandlers{""}{""}; } else { # Look among record handlers $handler = $PDBHandlers{$self->{creator}}{$self->{type}} || $PDBHandlers{""}{$self->{type}} || $PDBHandlers{$self->{creator}}{""} || $PDBHandlers{""}{""}; } if (defined($handler)) { bless $self, $handler; } else { # XXX - This should probably return 'undef' or something, # rather than die. die "No handler defined for creator \"$creator\", type \"$type\"\n"; } ## Read record/resource index # Read index header read $handle, $buf, RecIndexHeaderLen; my $next_index; my $numrecs; ($next_index, $numrecs) = unpack "N n", $buf; $self->{_numrecs} = $numrecs; # Read the index itself if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) { &_load_rsrc_index($self, $handle); } else { &_load_rec_index($self, $handle); } # Read the two NUL bytes # XXX - Actually, these are bogus. They don't appear in the # spec. The Right Thing to do is to ignore them, and use the # specified or calculated offsets, if they're sane. Sane == # appears later than the current position. # read $handle, $buf, 2; # $self->{"2NULs"} = $buf; # Read AppInfo block, if it exists if ($self->{_appinfo_offset} != 0) { &_load_appinfo_block($self, $handle); } # Read sort block, if it exists if ($self->{_sort_offset} != 0) { &_load_sort_block($self, $handle); } # Read record/resource list if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) { &_load_resources($self, $handle); } else { &_load_records($self, $handle); } # These keys were needed for parsing the file, but are not # needed any longer. Delete them. delete $self->{_index}; delete $self->{_numrecs}; delete $self->{_appinfo_offset}; delete $self->{_sort_offset}; delete $self->{_size}; $self->{'dirty'} = 0; return $self; } # _load_rec_index # Private function. Read the record index, for a record database sub _load_rec_index { my $pdb = shift; my $fh = shift; # Input file handle my $i; my $lastoffset = 0; # Read each record index entry in turn for ($i = 0; $i < $pdb->{_numrecs}; $i++) { my $buf; # Input buffer # Read the next record index entry my $offset; my $attributes; my @id; # Raw ID my $id; # Numerical ID my $entry = {}; # Parsed index entry read $fh, $buf, IndexRecLen; # The ID field is a bit weird: it's represented as 3 # bytes, but it's really a double word (long) value. ($offset, $attributes, @id) = unpack "N C C3", $buf; if ($offset == $lastoffset) { print STDERR "Record $i has same offset as previous one: $offset\n"; } $lastoffset = $offset; $entry->{offset} = $offset; $entry->{attributes}{expunged} = 1 if $attributes & 0x80; $entry->{attributes}{dirty} = 1 if $attributes & 0x40; $entry->{attributes}{deleted} = 1 if $attributes & 0x20; $entry->{attributes}{private} = 1 if $attributes & 0x10; # Attribute names as of PalmOS 5.0 ( see /Core/System/DataMgr.h ) $entry->{'attributes'}{'Delete'} = 1 if $attributes & 0x80; $entry->{'attributes'}{'Dirty'} = 1 if $attributes & 0x40; $entry->{'attributes'}{'Busy'} = 1 if $attributes & 0x20; $entry->{'attributes'}{'Secret'} = 1 if $attributes & 0x10; $entry->{id} = ($id[0] << 16) | ($id[1] << 8) | $id[2]; # The lower 4 bits of the attributes field are # overloaded: If the record has been deleted and/or # expunged, then bit 0x08 indicates whether the record # should be archived. Otherwise (if it's an ordinary, # non-deleted record), the lower 4 bits specify the # category that the record belongs in. if (($attributes & 0xa0) == 0) { $entry->{category} = $attributes & 0x0f; } else { $entry->{attributes}{archive} = 1 if $attributes & 0x08; } # Put this information on a temporary array push @{$pdb->{_index}}, $entry; } } # _load_rsrc_index # Private function. Read the resource index, for a resource database sub _load_rsrc_index { my $pdb = shift; my $fh = shift; # Input file handle my $i; # Read each resource index entry in turn for ($i = 0; $i < $pdb->{_numrecs}; $i++) { my $buf; # Input buffer # Read the next resource index entry my $type; my $id; my $offset; my $entry = {}; # Parsed index entry read $fh, $buf, IndexRsrcLen; ($type, $id, $offset) = unpack "a4 n N", $buf; $entry->{type} = $type; $entry->{id} = $id; $entry->{offset} = $offset; push @{$pdb->{_index}}, $entry; } } # _load_appinfo_block # Private function. Read the AppInfo block sub _load_appinfo_block { my $pdb = shift; my $fh = shift; # Input file handle my $len; # Length of AppInfo block my $buf; # Input buffer # Sanity check: make sure we're positioned at the beginning of # the AppInfo block if (tell($fh) > $pdb->{_appinfo_offset}) { die "Bad AppInfo offset: expected ", sprintf("0x%08x", $pdb->{_appinfo_offset}), ", but I'm at ", tell($fh), "\n"; } # Seek to the right place, if necessary if (tell($fh) != $pdb->{_appinfo_offset}) { seek $fh, $pdb->{_appinfo_offset}, 0; } # There's nothing that explicitly gives the size of the # AppInfo block. Rather, it has to be inferred from the offset # of the AppInfo block (previously recorded in # $pdb->{_appinfo_offset}) and whatever's next in the file. # That's either the sort block, the first data record, or the # end of the file. if ($pdb->{_sort_offset}) { # The next thing in the file is the sort block $len = $pdb->{_sort_offset} - $pdb->{_appinfo_offset}; } elsif ((defined $pdb->{_index}) && @{$pdb->{_index}}) { # There's no sort block; the next thing in the file is # the first data record $len = $pdb->{_index}[0]{offset} - $pdb->{_appinfo_offset}; } else { # There's no sort block and there are no records. The # AppInfo block goes to the end of the file. $len = $pdb->{_size} - $pdb->{_appinfo_offset}; } # Read the AppInfo block read $fh, $buf, $len; # Tell the real class to parse the AppInfo block $pdb->{appinfo} = $pdb->ParseAppInfoBlock($buf); } # _load_sort_block # Private function. Read the sort block. sub _load_sort_block { my $pdb = shift; my $fh = shift; # Input file handle my $len; # Length of sort block my $buf; # Input buffer # Sanity check: make sure we're positioned at the beginning of # the sort block if (tell($fh) > $pdb->{_sort_offset}) { die "Bad sort block offset: expected ", sprintf("0x%08x", $pdb->{_sort_offset}), ", but I'm at ", tell($fh), "\n"; } # Seek to the right place, if necessary if (tell($fh) != $pdb->{_sort_offset}) { seek $fh, $pdb->{_sort_offset}, 0; } # There's nothing that explicitly gives the size of the sort # block. Rather, it has to be inferred from the offset of the # sort block (previously recorded in $pdb->{_sort_offset}) # and whatever's next in the file. That's either the first # data record, or the end of the file. if (defined($pdb->{_index})) { # The next thing in the file is the first data record $len = $pdb->{_index}[0]{offset} - $pdb->{_sort_offset}; } else { # There are no records. The sort block goes to the end # of the file. $len = $pdb->{_size} - $pdb->{_sort_offset}; } # Read the AppInfo block read $fh, $buf, $len; # XXX - Check to see if the sort block has some predefined # structure. If so, it might be a good idea to parse the sort # block here. # Tell the real class to parse the sort block $pdb->{sort} = $pdb->ParseSortBlock($buf); } # _load_records # Private function. Load the actual data records, for a record database # (PDB) sub _load_records { my $pdb = shift; my $fh = shift; # Input file handle my $i; # Read each record in turn for ($i = 0; $i < $pdb->{_numrecs}; $i++) { my $len; # Length of record my $buf; # Input buffer # Sanity check: make sure we're where we think we # should be. if (tell($fh) > $pdb->{_index}[$i]{offset}) { die "Bad offset for record $i: expected ", sprintf("0x%08x", $pdb->{_index}[$i]{offset}), " but it's at ", sprintf("[0x%08x]", tell($fh)), "\n"; } if( $pdb->{_index}[$i]{offset} > $pdb->{_size} ) { die "corruption: Record $i beyond end of database!"; } # Seek to the right place, if necessary if (tell($fh) != $pdb->{_index}[$i]{offset}) { seek $fh, $pdb->{_index}[$i]{offset}, 0; } # Compute the length of the record: the last record # extends to the end of the file. The others extend to # the beginning of the next record. if ($i == $pdb->{_numrecs} - 1) { # This is the last record $len = $pdb->{_size} - $pdb->{_index}[$i]{offset}; } else { # This is not the last record $len = $pdb->{_index}[$i+1]{offset} - $pdb->{_index}[$i]{offset}; } # Read the record read $fh, $buf, $len; # Tell the real class to parse the record data. Pass # &ParseRecord all of the information from the index, # plus a "data" field with the raw record data. my $record; $record = $pdb->ParseRecord( %{$pdb->{_index}[$i]}, "data" => $buf, ); push @{$pdb->{records}}, $record; } } # _load_resources # Private function. Load the actual data resources, for a resource database # (PRC) sub _load_resources { my $pdb = shift; my $fh = shift; # Input file handle my $i; # Read each resource in turn for ($i = 0; $i < $pdb->{_numrecs}; $i++) { my $len; # Length of record my $buf; # Input buffer # Sanity check: make sure we're where we think we # should be. if (tell($fh) > $pdb->{_index}[$i]{offset}) { die "Bad offset for resource $i: expected ", sprintf("0x%08x", $pdb->{_index}[$i]{offset}), " but it's at ", sprintf("0x%08x", tell($fh)), "\n"; } if( $pdb->{_index}[$i]{offset} > $pdb->{_size} ) { die "corruption: Resource $i beyond end of database!"; } # Seek to the right place, if necessary if (tell($fh) != $pdb->{_index}[$i]{offset}) { seek $fh, $pdb->{_index}[$i]{offset}, 0; } # Compute the length of the resource: the last # resource extends to the end of the file. The others # extend to the beginning of the next resource. if ($i == $pdb->{_numrecs} - 1) { # This is the last resource $len = $pdb->{_size} - $pdb->{_index}[$i]{offset}; } else { # This is not the last resource $len = $pdb->{_index}[$i+1]{offset} - $pdb->{_index}[$i]{offset}; } # Read the resource read $fh, $buf, $len; # Tell the real class to parse the resource data. Pass # &ParseResource all of the information from the # index, plus a "data" field with the raw resource # data. my $resource; $resource = $pdb->ParseResource( %{$pdb->{_index}[$i]}, "data" => $buf, ); push @{$pdb->{resources}}, $resource; } } #' <-- For Emacs sub Write { my $self = shift; my $fname = shift; # Output file name my @record_data; my @deleted_records; die "Can't write a database with no name\n" unless $self->{name} ne ""; my $handle = $self->_open('>', $fname); return undef unless defined $handle; # Open file binmode $handle; # Write as binary file under MS-DOS # Get AppInfo block my $appinfo_block = $self->PackAppInfoBlock; # Get sort block my $sort_block = $self->PackSortBlock; my $index_len; # Get records or resources if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) { # Resource database my $resource; foreach $resource (@{$self->{resources}}) { my $type; my $id; my $data; # Get all the stuff that goes in the index, as # well as the resource data. $type = $resource->{type}; $id = $resource->{id}; $data = $self->PackResource($resource); push @record_data, [ $type, $id, $data ]; } # Figure out size of index $index_len = RecIndexHeaderLen + @record_data * IndexRsrcLen; } else { my $record; foreach $record (@{$self->{records}}) { my $attributes; my $id; my $data; # XXX - Should probably check the length of this # record and not add it to the record if it's 0. # Get all the stuff that goes in the index, as # well as the record data. $attributes = 0; if ($record->{attributes}{expunged} || $record->{attributes}{deleted}) { $attributes |= 0x08 if $record->{attributes}{archive}; } else { $attributes = ($record->{category} & 0x0f); } $attributes |= 0x80 if $record->{attributes}{expunged}; $attributes |= 0x40 if $record->{attributes}{dirty}; $attributes |= 0x20 if $record->{attributes}{deleted}; $attributes |= 0x10 if $record->{attributes}{private}; $attributes |= 0x80 if $record->{'attributes'}{'Delete'}; $attributes |= 0x40 if $record->{'attributes'}{'Dirty'}; $attributes |= 0x20 if $record->{'attributes'}{'Busy'}; $attributes |= 0x10 if $record->{'attributes'}{'Secret'}; $id = $record->{id}; $data = $self->PackRecord($record); if ($attributes & 0x80) { push @deleted_records, [ $attributes, $id, $data ]; } else { push @record_data, [ $attributes, $id, $data ]; } } # put deleted records at end (RT#101666) push @record_data, @deleted_records; # Figure out size of index $index_len = RecIndexHeaderLen + @record_data * IndexRecLen; } my $header; my $attributes = 0x0000; my $appinfo_offset; my $sort_offset; # Build attributes field $attributes = ($self->{attributes}{resource} ? 0x0001 : 0) | ($self->{attributes}{"read-only"} ? 0x0002 : 0) | ($self->{attributes}{"AppInfo dirty"} ? 0x0004 : 0) | ($self->{attributes}{backup} ? 0x0008 : 0) | ($self->{attributes}{"OK newer"} ? 0x0010 : 0) | ($self->{attributes}{reset} ? 0x0020 : 0) | ($self->{attributes}{open} ? 0x8000 : 0); $attributes |= 0x0001 if $self->{'attributes'}{'ResDB'}; $attributes |= 0x0002 if $self->{'attributes'}{'ReadOnly'}; $attributes |= 0x0004 if $self->{'attributes'}{'AppInfoDirty'}; $attributes |= 0x0008 if $self->{'attributes'}{'Backup'}; $attributes |= 0x0010 if $self->{'attributes'}{'OKToInstallNewer'}; $attributes |= 0x0020 if $self->{'attributes'}{'ResetAfterInstall'}; $attributes |= 0x0040 if $self->{'attributes'}{'CopyPrevention'}; $attributes |= 0x0080 if $self->{'attributes'}{'Stream'}; $attributes |= 0x0100 if $self->{'attributes'}{'Hidden'}; $attributes |= 0x0200 if $self->{'attributes'}{'LaunchableData'}; $attributes |= 0x0400 if $self->{'attributes'}{'Recyclable'}; $attributes |= 0x0800 if $self->{'attributes'}{'Bundle'}; $attributes |= 0x8000 if $self->{'attributes'}{'Open'}; # Calculate AppInfo block offset if ((!defined($appinfo_block)) || ($appinfo_block eq "")) { # There's no AppInfo block $appinfo_offset = 0; } else { # Offset of AppInfo block from start of file $appinfo_offset = HeaderLen + $index_len + 2; } # Calculate sort block offset if ((!defined($sort_block)) || ($sort_block eq "")) { # There's no sort block $sort_offset = 0; } else { # Offset of sort block... if ($appinfo_offset == 0) { # ...from start of file $sort_offset = HeaderLen + $index_len + 2; } else { # ...or just from start of AppInfo block $sort_offset = $appinfo_offset + length($appinfo_block); } } # Write header $header = pack "a32 n n N N N N N N a4 a4 N", $self->{name}, $attributes, $self->{version}, $self->{ctime} + EPOCH_1904, $self->{mtime} + EPOCH_1904, $self->{baktime} + EPOCH_1904, $self->{modnum}, $appinfo_offset, $sort_offset, $self->{type}, $self->{creator}, $self->{uniqueIDseed}; ; print $handle "$header"; # Write index header my $index_header; $index_header = pack "N n", 0, scalar @record_data; print $handle "$index_header"; # Write index my $rec_offset; # Offset of next record/resource # Calculate offset of first record/resource if ($sort_offset != 0) { $rec_offset = $sort_offset + length($sort_block); } elsif ($appinfo_offset != 0) { $rec_offset = $appinfo_offset + length($appinfo_block); } else { $rec_offset = HeaderLen + $index_len + 2; } if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) { # Resource database # Record database my $rsrc_data; foreach $rsrc_data (@record_data) { my $type; my $id; my $data; my $index_data; ($type, $id, $data) = @{$rsrc_data}; $index_data = pack "a4 n N", $type, $id, $rec_offset; print $handle "$index_data"; $rec_offset += length($data); } } else { # Record database my $rec_data; foreach $rec_data (@record_data) { my $attributes; my $data; my $id; my $index_data; # XXX - Probably shouldn't write this record if # length($data) == 0 ($attributes, $id, $data) = @{$rec_data}; if (length($data) == 0) { warn printf("Write: Warning: record 0x%08x has length 0\n", $id) } $index_data = pack "N C C3", $rec_offset, $attributes, ($id >> 16) & 0xff, ($id >> 8) & 0xff, $id & 0xff; print $handle "$index_data"; $rec_offset += length($data); } } # Write the two NULs if (length($self->{"2NULs"}) == 2) { print $handle $self->{"2NULs"}; } else { print $handle "\0\0"; } # Write AppInfo block print $handle $appinfo_block unless $appinfo_offset == 0; # Write sort block print $handle $sort_block unless $sort_offset == 0; # Write record/resource list my $record; foreach $record (@record_data) { my $data; if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) { # Resource database my $type; my $id; ($type, $id, $data) = @{$record}; } else { my $attributes; my $id; ($attributes, $id, $data) = @{$record}; } print $handle $data; } return $self; } # PDB::new_Record() # Create a new, initialized record, and return a reference to it. # The record is initially marked as being dirty, since that's usually # the Right Thing. sub new_Record { my $classname = shift; my $retval = {}; # Initialize the record $retval->{'category'} = 0; # Unfiled, by convention $retval->{'attributes'} = { # expunged => 0, dirty => 1, # Note: originally dirty 'Dirty' => 1, # deleted => 0, # private => 0, # archive => 0, }; $retval->{'id'} = 0; # Initially, no record ID return $retval; } #' sub is_Dirty { my $self = shift; # try the quick and easy tests first return 1 if $self->{'dirty'}; return 1 if $self->{'attributes'}{'AppInfoDirty'}; return 1 if $self->{'attributes'}{'AppInfo dirty'}; # okay, check the records. Note that resource entries appear to # have no dirty flags for us to use. if (!$self->{attributes}{resource} and !$self->{'attributes'}{'ResDB'}) { my $record; foreach $record (@{$self->{records}}) { return 1 if $record->{'attributes'}{'Dirty'}; return 1 if $record->{'attributes'}{'dirty'}; } } return 0; } #' # append_Record # Append the given records to the database's list of records. If no # records are given, create one, append it, and return a reference to # it. sub append_Record { my $self = shift; unless (@_) { # No arguments given. Create a new record. my $record = $self->new_Record; # Validate the unique ID. $self->_setUniqueID($record) if $record->{'id'} eq 0; push @{$self->{records}}, $record; # Update the "last modification time". $self->{mtime} = time; $self->{dirty} = 1; return $record; } # Validate the unique IDs. foreach my $record (@_) { $self->_setUniqueID($record) if $record->{'id'} eq 0; } # At least one argument was given. Append all of the arguments # to the list of records, and return the first one. push @{$self->{records}}, @_; # Update the "last modification time". $self->{mtime} = time; $self->{'dirty'} = 1; return $_[0]; } sub _setUniqueID { my($self, $record) = @_; # Bump the seed to prevent a uniqueIDseed of 0 which represents # an unassigned uniqueID. # XXX IMHO this just couldn't happen given the way the seed it's # generated. But if Palm OS goes this way maybe it's better to do # the same. $self->{'uniqueIDseed'}++; # Check for wrap around. Remember that an uniqueID is made of only 24 bits. $self->{'uniqueIDseed'} = (dmRecordIDReservedRange + 1) << 12 if ($self->{'uniqueIDseed'} & 0xFF000000); # Copy the seed into the new record. $record->{'id'} = $self->{'uniqueIDseed'}; } # new_Resource # Create a new, initialized resource, and return a reference to it. sub new_Resource { my $classname = shift; my $retval = {}; # Initialize the resource $retval->{type} = "\0\0\0\0"; $retval->{id} = 0; return $retval; } #' # append_Resource # Append the given resources to the database's list of resources. If no # resources are given, create one, append it, and return a reference to # it. sub append_Resource { my $self = shift; unless (@_) { # No arguments given. Create a new resource my $resource = $self->new_Resource; push @{$self->{resources}}, $resource; # Update the "last modification time". $self->{mtime} = time; $self->{'dirty'} = 1; return $resource; } # At least one argument was given. Append all of the arguments # to the list of resources, and return the first one. push @{$self->{resources}}, @_; # Update the "last modification time". $self->{mtime} = time; $self->{'dirty'} = 1; return $_[0]; } # findRecordByID # Returns a reference to the record with the given ID, or 'undef' if # it doesn't exist. sub findRecordByID { my $self = shift; my $id = shift; return undef if $id eq ""; for (@{$self->{records}}) { next unless $_->{id} == $id; return $_; # Found it } return undef; # Not found } #' # delete_Record # $pdb->delete_Record($record ?, $expunge?) # # Mark the given record for deletion. If $expunge is true, mark the # record for deletion without an archive. sub delete_Record { my $self = shift; my $record = shift; my $expunge = shift; $record->{attributes}{deleted} = 1; if ($expunge) { $record->{attributes}{expunged} = 1; $record->{attributes}{archive} = 0; } else { $record->{attributes}{expunged} = 0; $record->{attributes}{archive} = 1; } # Update the "last modification time". $self->{mtime} = time; $self->{'dirty'} = 1; } #' sub remove_Record($$) { my $self = shift; my $record = shift; for (my $i = 0; $i <= $#{$self->{records}}; $i ++) { if ($self->{records}->[$i] == $record) { # make a copy of the records array. This is really necessary # because there's frequently something using the records reference # for iteration purposes (like the doc example) and we can't # just start splicing that apart (tried, failed). # So we have to make a new copy. This does, unfortunately, # make remove_Record() more expensive that you'd expect. $self->{records} = [ @{$self->{records}} ]; # remove the record index. splice @{$self->{records}}, $i, 1; $self->{mtime} = time; $self->{'dirty'} = 1; last; } } } 1; __END__ =head1 NAME Palm::PDB - Parse Palm database files =head1 VERSION This document describes version 1.400 of Palm::PDB, released March 7, 2015 as part of Palm-PDB version 1.400. =head1 SYNOPSIS use Palm::PDB; use SomeHelperClass; $pdb = Palm::PDB->new; $pdb->Load("myfile.pdb"); # Manipulate records in $pdb $pdb->Write("myotherfile.pdb"); (Note: yes, you do want to use C, even if you're dealing with some other type of database. $pdb will be reblessed to the appropriate type by C<$pdb-ELoad>.) =head1 DESCRIPTION The Palm::PDB module provides a framework for reading and writing database files for use on PalmOS devices such as the PalmPilot. It can read and write both Palm Database (C<.pdb>) and Palm Resource (C<.prc>) files. By itself, the PDB module is not terribly useful; it is intended to be used in conjunction with supplemental modules for specific types of databases, such as Palm::Raw or Palm::Memo. The Palm::PDB module encapsulates the common work of parsing the structure of a Palm database. The L function reads the file, then passes the individual chunks (header, records, etc.) to application-specific functions for processing. Similarly, the L function calls application-specific functions to get the individual chunks, then writes them to a file. =head1 METHODS =head2 new $new = Palm::PDB->new; Creates a new PDB. $new is a reference to an anonymous hash. Some of its elements have special significance. See L. =head2 RegisterPDBHandlers &Palm::PDB::RegisterPDBHandlers("classname", typespec...); Typically: &Palm::PDB::RegisterPDBHandlers(__PACKAGE__, [ "FooB", "DATA" ], ); The $pdb->L method acts as a virtual constructor. When it reads the header of a C<.pdb> file, it looks up the file's creator and type in a set of tables, and reblesses $pdb into a class capable of parsing the application-specific parts of the file (AppInfo block, records, etc.) RegisterPDBHandlers() adds entries to these tables; it says that any file whose creator and/or type match any of the Is (there may be several) should be reblessed into the class I. Note that RegisterPDBHandlers() applies only to record databases (C<.pdb> files). For resource databases, see L. RegisterPDBHandlers() is typically called in the import() function of a helper class. In this case, the class is registering itself, and it is simplest just to use C<__PACKAGE__> for the package name: package PalmFoo; use Palm::PDB; sub import { &Palm::PDB::RegisterPDBHandlers(__PACKAGE__, [ "FooZ", "DATA" ] ); } A I can be either a string, or an anonymous array with two elements. If it is an anonymous array, then the first element is the file's creator; the second element is its type. If a I is a string, it is equivalent to specifying that string as the database's creator, and a wildcard as its type. The creator and type should be either four-character strings, or the empty string. An empty string represents a wildcard. Thus: &Palm::PDB::RegisterPDBHandlers("MyClass", [ "fOOf", "DATA" ], [ "BarB", "" ], [ "", "BazQ" ], "Fred" ); Class MyClass will handle: =over 4 =item * Databases whose creator is C and whose type is C. =item * Databases whose creator is C, of any type. =item * Databases with any creator whose type is C. =item * Databases whose creator is C, of any type. =back =for html =head2 RegisterPRCHandlers &Palm::PDB::RegisterPRCHandlers("classname", typespec...); Typically: &Palm::PDB::RegisterPRCHandlers(__PACKAGE__, [ "FooZ", "CODE" ], ); RegisterPRCHandlers() is similar to L, but specifies a class to handle resource database (C<.prc>) files. A class for parsing applications should begin with: package PalmApps; use Palm::PDB; sub import { &Palm::PDB::RegisterPRCHandlers(__PACKAGE__, [ "", "appl" ] ); } =head2 Load $pdb->Load($filename); Reads the file C<$filename>, parses it, reblesses $pdb to the appropriate class, and invokes appropriate methods to parse the application-specific parts of the database (see L). C<$filename> may also be an open file handle (as long as it's seekable). This allows for manipulating databases in memory structures. Load() uses the Is given to RegisterPDBHandlers() and RegisterPRCHandlers() when deciding how to rebless $pdb. For record databases, it uses the Is passed to RegisterPDBHandlers(), and for resource databases, it uses the Is passed to RegisterPRCHandlers(). Load() looks for matching Is in the following order, from most to least specific: =over 4 =item 1 A I that specifies both the database's creator and its type exactly. =item 2 A I that specifies the database's type and has a wildcard for the creator (this is rarely used). =item 3 A I that specifies the database's creator and has a wildcard for the type. =item 4 A I that has wildcards for both the creator and type. =back =for html Thus, if the database has creator "FooZ" and type "DATA", Load() will first look for "FooZ"/"DATA", then ""/"DATA", then "FooZ"/"", and finally will fall back on ""/"" (the universal default). After Load() returns, $pdb may contain the following fields: =over =item $pdb-E{Z<>"name"Z<>} The name of the database. =item $pdb-E{Z<>"attributes"Z<>}{Z<>"ResDB"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"ReadOnly"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"AppInfoDirty"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Backup"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"OKToInstallNewer"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"ResetAfterInstall"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"CopyPrevention"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Stream"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Hidden"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"LaunchableData"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Recyclable"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Bundle"Z<>} =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Open"Z<>} These are the attribute flags from the database header. Each is true iff the corresponding flag is set. The "LaunchableData" attribute is set on PQAs. =item $pdb-E{Z<>"version"Z<>} The database's version number. An integer. =item $pdb-E{Z<>"ctime"Z<>} =item $pdb-E{Z<>"mtime"Z<>} =item $pdb-E{Z<>"baktime"Z<>} The database's creation time, last modification time, and time of last backup, in Unix C format (seconds since Jan. 1, 1970). =item $pdb-E{Z<>"modnum"Z<>} The database's modification number. An integer. =item $pdb-E{Z<>"type"Z<>} The database's type. A four-character string. =item $pdb-E{Z<>"creator"Z<>} The database's creator. A four-character string. =item $pdb-E{Z<>"uniqueIDseed"Z<>} The database's unique ID seed. An integer. =item $pdb-E{Z<>"2NULs"Z<>} The two NUL bytes that appear after the record index and the AppInfo block. Included here because every once in a long while, they are not NULs, for some reason. =item $pdb-E{Z<>"appinfo"Z<>} The AppInfo block, as returned by the $pdb->ParseAppInfoBlock() helper method. =item $pdb-E{Z<>"sort"Z<>} The sort block, as returned by the $pdb->ParseSortBlock() helper method. =item @{$pdb->{"records"}Z<>} The list of records in the database, as returned by the $pdb->ParseRecord() helper method. Resource databases do not have this. =item @{$pdb->{"resources"}Z<>} The list of resources in the database, as returned by the $pdb->ParseResource() helper method. Record databases do not have this. =back All of these fields may be set by hand, but should conform to the format given above. =for html =head2 Write $pdb->Write($filename); Invokes methods in helper classes to get the application-specific parts of the database, then writes the database to the file C<$filename>. C<$filename> may also be an open file handle (as long as it's seekable). This allows for manipulating databases in memory structures. Write() uses the following helper methods: =over =item PackAppInfoBlock() =item PackSortBlock() =item PackResource() or PackRecord() =back =for html See also L. =head2 new_Record $record = Palm::PDB->new_Record(); Creates a new record, with the bare minimum needed: $record->{'category'} $record->{'attributes'}{'Dirty'} $record->{'id'} The ``Dirty'' attribute is originally set, since this function will usually be called to create records to be added to a database. C does B add the new record to a PDB. For that, you want C. =head2 is_Dirty $pdb->Write( $fname ) if $pdb->is_Dirty(); Returns non-zero if any of the in-memory elements of the database have been changed. This includes changes via function calls (any call that changes the C<$pdb>'s "last modification" time) as well as testing the "dirty" status of attributes where possible (i.e. AppInfo, records, but not resource entries). =head2 append_Record $record = $pdb->append_Record; $record2 = $pdb->append_Record($record1); If called without any arguments, creates a new record with L, and appends it to $pdb. If given a reference to a record, appends that record to @{$pdb->{records}Z<>}. Returns a reference to the newly-appended record. This method updates $pdb's "last modification" time. =head2 new_Resource $resource = Palm::PDB->new_Resource(); Creates a new resource and initializes $resource->{type} $resource->{id} =head2 append_Resource $resource = $pdb->append_Resource; $resource2 = $pdb->append_Resource($resource1); If called without any arguments, creates a new resource with L, and appends it to $pdb. If given a reference to a resource, appends that resource to @{$pdb->{resources}Z<>}. Returns a reference to the newly-appended resource. This method updates $pdb's "last modification" time. =head2 findRecordByID $record = $pdb->findRecordByID($id); Looks through the list of records in $pdb, and returns a reference to the record with ID $id, or the undefined value if no such record was found. =head2 delete_Record $pdb->delete_Record($record, $expunge); Marks $record for deletion, so that it will be deleted from the database at the next sync. If $expunge is false or omitted, the record will be marked for deletion with archival. If $expunge is true, the record will be marked for deletion without archival. This method updates $pdb's "last modification" time. =head2 remove_Record for (@{ $pdb->{'records'} }) { $pdb->remove_Record( $_ ) if $_->{attributes}{deleted}; } Removes C<$record> from the database. This differs from C in that it's an actual deletion rather than just setting a flag. This method updates $pdb's "last modification" time. =head1 HELPER CLASS METHODS C<< $pdb->Load() >> reblesses C<$pdb> into a new class. This helper class is expected to convert raw data from the database into parsed representations of it, and vice-versa. A helper class must have all of the methods listed below. The L class is useful if you don't want to define all of the required methods. =head2 ParseAppInfoBlock $appinfo = $pdb->ParseAppInfoBlock($buf); C<$buf> is a string of raw data. ParseAppInfoBlock() should parse this data and return it, typically in the form of a reference to an object or to an anonymous hash. This method will not be called if the database does not have an AppInfo block. The return value from ParseAppInfoBlock() will be accessible as C<< $pdb->{appinfo} >>. =head2 PackAppInfoBlock $buf = $pdb->PackAppInfoBlock(); This is the converse of ParseAppInfoBlock(). It takes C<$pdb>'s AppInfo block, C<< $pdb->{appinfo} >>, and returns a string of binary data that can be written to the database file. =head2 ParseSortBlock $sort = $pdb->ParseSortBlock($buf); C<$buf> is a string of raw data. ParseSortBlock() should parse this data and return it, typically in the form of a reference to an object or to an anonymous hash. This method will not be called if the database does not have a sort block. The return value from ParseSortBlock() will be accessible as C<< $pdb->{sort} >>. =head2 PackSortBlock $buf = $pdb->PackSortBlock(); This is the converse of ParseSortBlock(). It takes C<$pdb>'s sort block, C<< $pdb->{sort} >>, and returns a string of raw data that can be written to the database file. =head2 ParseRecord $record = $pdb->ParseRecord( offset => $offset, # Record's offset in file attributes => # Record attributes { expunged => bool, # True iff expunged dirty => bool, # True iff dirty deleted => bool, # True iff deleted private => bool, # True iff private archive => bool, # True iff to be archived }, category => $category, # Record's category number id => $id, # Record's unique ID data => $buf, # Raw record data ); ParseRecord() takes the arguments listed above and returns a parsed representation of the record, typically as a reference to a record object or anonymous hash. The output from ParseRecord() will be appended to C<< @{$pdb->{records}Z<>} >>. The records appear in this list in the same order as they appear in the file. C<$offset> argument is not normally useful, but is included for completeness. The fields in C<%$attributes> are boolean values. They are true iff the record has the corresponding flag set. C<$category> is an integer in the range 0-15, which indicates which category the record belongs to. This is normally an index into a table given at the beginning of the AppInfo block. A typical ParseRecord() method has this general form: sub ParseRecord { my $self = shift my %record = @_; # Parse $self->{data} and put the fields into new fields in # $self. delete $record{data}; # No longer useful return \%record; } =head2 PackRecord $buf = $pdb->PackRecord($record); The converse of ParseRecord(). PackRecord() takes a record as returned by ParseRecord() and returns a string of raw data that can be written to the database file. PackRecord() is never called when writing a resource database. =head2 ParseResource $record = $pdb->ParseResource( type => $type, # Resource type id => $id, # Resource ID offset => $offset, # Resource's offset in file data => $buf, # Raw resource data ); ParseResource() takes the arguments listed above and returns a parsed representation of the resource, typically as a reference to a resource object or anonymous hash. The output from ParseResource() will be appended to C<< @{$pdb->{resources}Z<>} >>. The resources appear in this list in the same order as they appear in the file. $type is a four-character string giving the resource's type. $id is an integer that uniquely identifies the resource amongst others of its type. $offset is not normally useful, but is included for completeness. =head2 PackResource $buf = $pdb->PackResource($resource); The converse of ParseResource(). PackResource() takes a resource as returned by PackResource() and returns a string of raw data that can be written to the database file. PackResource() is never called when writing a record database. =head1 SEE ALSO L L L L L L F, in the ColdSync distribution. The Virtual Constructor (aka Factory Method) pattern is described in F, by Erich Gamma I, Addison-Wesley. =head1 CONFIGURATION AND ENVIRONMENT Palm::PDB requires no configuration files or environment variables. =head1 INCOMPATIBILITIES None reported. =head1 BUGS AND LIMITATIONS These functions die too easily. They should return an error code. Database manipulation is still an arcane art. It may be possible to parse sort blocks further. =head1 AUTHORS Andrew Arensburger C<< >> Currently maintained by Christopher J. Madsen C<< >> Please report any bugs or feature requests to S >>> or through the web interface at L<< http://rt.cpan.org/Public/Bug/Report.html?Queue=Palm-PDB >>. You can follow or contribute to Palm-PDB's development at L<< https://github.com/madsen/Palm-PDB >>. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Andrew Arensburger. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENSE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. =cut Palm-PDB-1.400/lib/Palm/Raw.pm0000644000175000017500000001360412476641116013660 0ustar cjmcjmpackage Palm::Raw; # # Perl class for dealing with "raw" PDB databases. A "raw" database is # one where the AppInfo and sort blocks, and all of the # records/resources, are just strings of bytes. # This is useful as a default PDB handler, for cases where you want to # be able to handle any kind of database in a generic fashion. # You may also find it useful to subclass this class, for cases where # you don't care about every type of thing in a database. # # Copyright (C) 1999, 2000, Andrew Arensburger. # # This program is free software; you can redistribute it and/or modify # it under the same terms as Perl itself. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See either the # GNU General Public License or the Artistic License for more details. use 5.006; use strict; use Palm::PDB; our $VERSION = '1.400'; # VERSION # This file is part of Palm-PDB 1.400 (March 7, 2015) our @ISA = qw( Palm::PDB ); # ABSTRACT: Handler for "raw" Palm databases #' sub import { # This package handles any PDB. &Palm::PDB::RegisterPDBHandlers(__PACKAGE__, [ "", "" ] ); } # sub new # sub new_Record # These are just inherited. sub ParseAppInfoBlock { my $self = shift; my $data = shift; return $data; } sub ParseSortBlock { my $self = shift; my $data = shift; return $data; } sub ParseRecord { my $self = shift; my %record = @_; return \%record; } sub ParseResource { my $self = shift; my %resource = @_; return \%resource; } sub PackAppInfoBlock { my $self = shift; return $self->{appinfo}; } sub PackSortBlock { my $self = shift; return $self->{sort}; } sub PackRecord { my $self = shift; my $record = shift; return $record->{data}; } sub PackResource { my $self = shift; my $resource = shift; return $resource->{data}; } 1; __END__ =head1 NAME Palm::Raw - Handler for "raw" Palm databases =head1 VERSION This document describes version 1.400 of Palm::Raw, released March 7, 2015 as part of Palm-PDB version 1.400. =head1 SYNOPSIS use Palm::Raw; For standalone programs. use Palm::Raw(); @ISA = qw( Palm::Raw ); For Palm::PDB helper modules. =head1 DESCRIPTION The Raw PDB handler is a helper class for the Palm::PDB package. It is intended as a generic handler for any database, or as a fallback default handler. If you have a standalone program and want it to be able to parse any type of database, use use Palm::Raw; If you are using Palm::Raw as a parent class for your own database handler, use use Palm::Raw(); If you omit the parentheses, Palm::Raw will register itself as the default handler for all databases, which is probably not what you want. The Raw handler does no processing on the database whatsoever. The AppInfo block, sort block, records and resources are simply strings, raw data from the database. By default, the Raw handler only handles record databases (.pdb files). If you want it to handle resource databases (.prc files) as well, you need to call &Palm::PDB::RegisterPRCHandlers("Palm::Raw", ""); in your script. =head2 AppInfo block $pdb->{appinfo} This is a scalar, the raw data of the AppInfo block. =head2 Sort block $pdb->{sort} This is a scalar, the raw data of the sort block. =head2 Records @{$pdb->{records}Z<>}; Each element in the "records" array is a reference-to-hash. In addition to the standard keys ("attributes", "category", and "id"), this hash contains the key "data"; its value is a string with the raw record data. =head2 Resources @{$pdb->{resources}Z<>}; Each element in the "resources" array is a reference-to-hash. In addition to the standard keys ("type" and "id"), it contains the key "data"; its value is a string with the raw resource data. =head1 SEE ALSO L =head1 CONFIGURATION AND ENVIRONMENT Palm::Raw requires no configuration files or environment variables. =head1 INCOMPATIBILITIES None reported. =head1 BUGS AND LIMITATIONS No bugs have been reported. =head1 AUTHORS Andrew Arensburger C<< >> Currently maintained by Christopher J. Madsen C<< >> Please report any bugs or feature requests to S >>> or through the web interface at L<< http://rt.cpan.org/Public/Bug/Report.html?Queue=Palm-PDB >>. You can follow or contribute to Palm-PDB's development at L<< https://github.com/madsen/Palm-PDB >>. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Andrew Arensburger. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENSE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. =cut Palm-PDB-1.400/examples/0000755000175000017500000000000012476641116012744 5ustar cjmcjmPalm-PDB-1.400/examples/pdbdump-raw0000755000175000017500000005206012476641116015117 0ustar cjmcjm#!/usr/bin/env perl # # Dump a Palm PDB or PRC database. use 5.006; use strict; use Palm::PDB; use Palm::Raw; our $VERSION = '1.400'; # VERSION # This file is part of Palm-PDB 1.400 (March 7, 2015) our ( %PDBHandlers, %PRCHandlers, $hexdump ); *PDBHandlers = *Palm::PDB::PDBHandlers; *PRCHandlers = *Palm::PDB::PRCHandlers; &Palm::PDB::RegisterPRCHandlers("Palm::Raw", [ "", "" ] ); $hexdump = 1; # By default, print hex dumps of everything # Parse command-line arguments my $arg; while (($arg = $ARGV[0]) =~ /^-/) { $arg = shift; if (($arg eq "-h") || ($arg eq "-help") || ($arg eq "--help")) { print < Load (e.g., -MPalm::Address) EOT #' exit 0; } elsif ($arg =~ /^-M/) { eval "use $';"; } elsif ($arg eq "-nohex") { $hexdump = 0; } else { die "Unrecognized option: $arg\n"; } } my $fname = shift; die "No such file: $fname\n" if ! -f $fname; my $EPOCH_1904 = 2082844800; # Difference between Palm's # epoch (Jan. 1, 1904) and # Unix's epoch (Jan. 1, 1970), # in seconds. my $HeaderLen = 32+2+2+(9*4); # Size of database header my $RecIndexHeaderLen = 6; # Size of record index header my $IndexRecLen = 8; # Length of record index entry my $IndexRsrcLen = 10; # Length of resource index entry #%PDBHandlers = (); # Record handler map #%PRCHandlers = (); # Resource handler map #&Palm::PDB::rawread($fname); &rawread($fname); #package Palm::PDB; # XXX - Gross hack! sub rawread { my $self = new Palm::Raw; my $fname = shift; # Filename to read from my $buf; # Buffer into which to read stuff # Open database file open PDB, "< $fname" or die "Can't open \"$fname\": $!\n"; binmode PDB; # Parse as binary file under MS-DOS # Get the size of the file. It'll be useful later seek PDB, 0, 2; # 2 == SEEK_END. Seek to the end. $self->{_size} = tell PDB; print "File size: $self->{_size}\n"; seek PDB, 0, 0; # 0 == SEEK_START. Rewind to the beginning. # Read header my $name; my $attributes; my $version; my $ctime; my $mtime; my $baktime; my $modnum; my $appinfo_offset; my $sort_offset; my $type; my $creator; my $uniqueIDseed; read PDB, $buf, $HeaderLen; # Read the PDB header print "Database header:\n"; if ($hexdump) { &hexdump(" ", $buf); print "\n"; } # Split header into its component fields ($name, $attributes, $version, $ctime, $mtime, $baktime, $modnum, $appinfo_offset, $sort_offset, $type, $creator, $uniqueIDseed) = unpack "a32 n n N N N N N N a4 a4 N", $buf; ($self->{name} = $name) =~ s/\0*$//; $self->{attributes}{resource} = 1 if $attributes & 0x0001; $self->{attributes}{"read-only"} = 1 if $attributes & 0x0002; $self->{attributes}{"AppInfo dirty"} = 1 if $attributes & 0x0004; $self->{attributes}{backup} = 1 if $attributes & 0x0008; $self->{attributes}{"OK newer"} = 1 if $attributes & 0x0010; $self->{attributes}{reset} = 1 if $attributes & 0x0020; $self->{attributes}{open} = 1 if $attributes & 0x0040; $self->{attributes}{launchable} = 1 if $attributes & 0x0200; $self->{version} = $version; $self->{ctime} = $ctime - $EPOCH_1904; $self->{mtime} = $mtime - $EPOCH_1904; $self->{baktime} = $baktime - $EPOCH_1904; $self->{modnum} = $modnum; # _appinfo_offset and _sort_offset are private fields $self->{_appinfo_offset} = $appinfo_offset; $self->{_sort_offset} = $sort_offset; $self->{type} = $type; $self->{creator} = $creator; $self->{uniqueIDseed} = $uniqueIDseed; print <{attributes}{launchable}; print " OPEN" if $self->{attributes}{open}; print " RESET" if $self->{attributes}{reset}; print " OKNEWER" if $self->{attributes}{"OK newer"}; print " BACKUP" if $self->{attributes}{backup}; print " APPINFO-DIRTY" if $self->{attributes}{"AppInfo dirty"}; print " READ-ONLY" if $self->{attributes}{"read-only"}; print " RESOURCE" if $self->{attributes}{resource}; print "\n"; print <{attributes}{resource}) { # Look among resource handlers $handler = $PRCHandlers{$self->{creator}}{$self->{type}} || $PRCHandlers{undef}{$self->{type}} || $PRCHandlers{$self->{creator}}{""} || $PRCHandlers{""}{""}; } else { # Look among record handlers $handler = $PDBHandlers{$self->{creator}}{$self->{type}} || $PDBHandlers{""}{$self->{type}} || $PDBHandlers{$self->{creator}}{""} || $PDBHandlers{""}{""}; } if (defined($handler)) { bless $self, $handler; } else { # XXX - This should probably return 'undef' or something, # rather than die. die "No handler defined for creator \"$creator\", type \"$type\"\n"; } ## Read record/resource index # Read index header read PDB, $buf, $RecIndexHeaderLen; print "Record/resource index header:\n"; if ($hexdump) { &hexdump(" ", $buf); print "\n"; } my $next_index; my $numrecs; ($next_index, $numrecs) = unpack "N n", $buf; $self->{_numrecs} = $numrecs; print <{attributes}{resource}) { &_load_rsrc_index($self, \*PDB); } else { &_load_rec_index($self, \*PDB); } # Ignore the two NUL bytes that are usually here. We'll seek() # around them later. # Read AppInfo block, if it exists if ($self->{_appinfo_offset} != 0) { &_load_appinfo_block($self, \*PDB); } # Read sort block, if it exists if ($self->{_sort_offset} != 0) { &_load_sort_block($self, \*PDB); } # Read record/resource list if ($self->{attributes}{resource}) { &_load_resources($self, \*PDB); } else { &_load_records($self, \*PDB); } # These keys were needed for parsing the file, but are not # needed any longer. Delete them. delete $self->{_index}; delete $self->{_numrecs}; delete $self->{_appinfo_offset}; delete $self->{_sort_offset}; delete $self->{_size}; close PDB; } # _load_rec_index # Private function. Read the record index, for a record database sub _load_rec_index { my $pdb = shift; my $fh = shift; # Input file handle my $i; my $lastoffset = 0; print "Record index:\n"; # Read each record index entry in turn for ($i = 0; $i < $pdb->{_numrecs}; $i++) { my $buf; # Input buffer print " Record index entry $i\n"; # Read the next record index entry my $offset; my $attributes; my @id; # Raw ID my $id; # Numerical ID my $entry = {}; # Parsed index entry read $fh, $buf, $IndexRecLen; if ($hexdump) { &hexdump(" ", $buf); print "\n"; } # The ID field is a bit weird: it's represented as 3 # bytes, but it's really a double word (long) value. ($offset, $attributes, @id) = unpack "N C C3", $buf; if ($offset == $lastoffset) { print STDERR "Record $i has same offset as previous one: $offset\n"; } $lastoffset = $offset; $entry->{offset} = $offset; $entry->{attributes}{expunged} = 1 if $attributes & 0x80; $entry->{attributes}{dirty} = 1 if $attributes & 0x40; $entry->{attributes}{deleted} = 1 if $attributes & 0x20; $entry->{attributes}{private} = 1 if $attributes & 0x10; $entry->{id} = ($id[0] << 16) | ($id[1] << 8) | $id[2]; # The lower 4 bits of the attributes field are # overloaded: If the record has been deleted and/or # expunged, then bit 0x08 indicates whether the record # should be archived. Otherwise (if it's an ordinary, # non-deleted record), the lower 4 bits specify the # category that the record belongs in. if (($attributes & 0xa0) == 0) { $entry->{category} = $attributes & 0x0f; } else { $entry->{attributes}{archive} = 1 if $attributes & 0x08; } print <{attributes}}]} Category: $entry->{category} ID: @{[sprintf("0x%02x%02x%02x", @id)]} EOT # Put this information on a temporary array push @{$pdb->{_index}}, $entry; } } # XXX - Make this print out debugging information # _load_rsrc_index # Private function. Read the resource index, for a resource database sub _load_rsrc_index { my $pdb = shift; my $fh = shift; # Input file handle my $i; print "Resource index:\n"; # Read each resource index entry in turn for ($i = 0; $i < $pdb->{_numrecs}; $i++) { my $buf; # Input buffer print " Resource index entry $i\n"; # Read the next resource index entry my $type; my $id; my $offset; my $entry = {}; # Parsed index entry read $fh, $buf, $IndexRsrcLen; if ($hexdump) { &hexdump(" ", $buf); print "\n"; } ($type, $id, $offset) = unpack "a4 n N", $buf; $entry->{type} = $type; $entry->{id} = $id; $entry->{offset} = $offset; print <{_index}}, $entry; } } # _load_appinfo_block # Private function. Read the AppInfo block sub _load_appinfo_block { my $pdb = shift; my $fh = shift; # Input file handle my $len; # Length of AppInfo block my $buf; # Input buffer print "AppInfo block:\n"; # Sanity check: make sure we're positioned at the beginning of # the AppInfo block if (tell($fh) > $pdb->{_appinfo_offset}) { die "Bad AppInfo offset: expected ", sprintf("0x%08x", $pdb->{_appinfo_offset}), ", but I'm at ", tell($fh), "\n"; } # Seek to the right place, if necessary if (tell($fh) != $pdb->{_appinfo_offset}) { seek PDB, $pdb->{_appinfo_offset}, 0; } # There's nothing that explicitly gives the size of the # AppInfo block. Rather, it has to be inferred from the offset # of the AppInfo block (previously recorded in # $pdb->{_appinfo_offset}) and whatever's next in the file. # That's either the sort block, the first data record, or the # end of the file. if ($pdb->{_sort_offset}) { # The next thing in the file is the sort block $len = $pdb->{_sort_offset} - $pdb->{_appinfo_offset}; } elsif ((defined $pdb->{_index}) && @{$pdb->{_index}}) { # There's no sort block; the next thing in the file is # the first data record $len = $pdb->{_index}[0]{offset} - $pdb->{_appinfo_offset}; } else { # There's no sort block and there are no records. The # AppInfo block goes to the end of the file. $len = $pdb->{_size} - $pdb->{_appinfo_offset}; } # Read the AppInfo block read $fh, $buf, $len; if ($hexdump) { &hexdump(" ", $buf); print "\n"; } # Tell the real class to parse the AppInfo block $pdb->{appinfo} = $pdb->ParseAppInfoBlock($buf); # Print out the parsed values if (ref($pdb->{appinfo}) ne "") { &dumphash($pdb->{appinfo}, "\t"); print "\n"; } } # _load_sort_block # Private function. Read the sort block. sub _load_sort_block { my $pdb = shift; my $fh = shift; # Input file handle my $len; # Length of sort block my $buf; # Input buffer print "Sort block:\n"; # Sanity check: make sure we're positioned at the beginning of # the sort block if (tell($fh) > $pdb->{_sort_offset}) { die "Bad sort block offset: expected ", sprintf("0x%08x", $pdb->{_sort_offset}), ", but I'm at ", tell($fh), "\n"; } # Seek to the right place, if necessary if (tell($fh) != $pdb->{_sort_offset}) { seek PDB, $pdb->{_sort_offset}, 0; } # There's nothing that explicitly gives the size of the sort # block. Rather, it has to be inferred from the offset of the # sort block (previously recorded in $pdb->{_sort_offset}) # and whatever's next in the file. That's either the first # data record, or the end of the file. if (defined($pdb->{_index})) { # The next thing in the file is the first data record $len = $pdb->{_index}[0]{offset} - $pdb->{_sort_offset}; } else { # There are no records. The sort block goes to the end # of the file. $len = $pdb->{_size} - $pdb->{_sort_offset}; } # Read the AppInfo block read $fh, $buf, $len; if ($hexdump) { &hexdump(" ", $buf); print "\n"; } # XXX - Check to see if the sort block has some predefined # structure. If so, it might be a good idea to parse the sort # block here. # Tell the real class to parse the sort block $pdb->{sort} = $pdb->ParseSortBlock($buf); } # _load_records # Private function. Load the actual data records, for a record database # (PDB) sub _load_records { my $pdb = shift; my $fh = shift; # Input file handle my $i; print "Records:\n"; # Read each record in turn for ($i = 0; $i < $pdb->{_numrecs}; $i++) { my $len; # Length of record my $buf; # Input buffer print " Record $i\n"; # Sanity check: make sure we're where we think we # should be. if (tell($fh) > $pdb->{_index}[$i]{offset}) { # XXX - The two NULs are really optional. # die "Bad offset for record $i: expected ", # sprintf("0x%08x", # $pdb->{_index}[$i]{offset}), # " but it's at ", # sprintf("[0x%08x]", tell($fh)), "\n"; } # Seek to the right place, if necessary if (tell($fh) != $pdb->{_index}[$i]{offset}) { seek PDB, $pdb->{_index}[$i]{offset}, 0; } # Compute the length of the record: the last record # extends to the end of the file. The others extend to # the beginning of the next record. if ($i == $pdb->{_numrecs} - 1) { # This is the last record $len = $pdb->{_size} - $pdb->{_index}[$i]{offset}; } else { # This is not the last record $len = $pdb->{_index}[$i+1]{offset} - $pdb->{_index}[$i]{offset}; } # Read the record read $fh, $buf, $len; if ($hexdump) { &hexdump(" ", $buf); print "\n"; } # Tell the real class to parse the record data. Pass # &ParseRecord all of the information from the index, # plus a "data" field with the raw record data. my $record; $record = $pdb->ParseRecord( %{$pdb->{_index}[$i]}, "data" => $buf, ); push @{$pdb->{records}}, $record; # Print out the parsed values &dumphash($record, "\t"); print "\n"; } } # _load_resources # Private function. Load the actual data resources, for a resource database # (PRC) sub _load_resources { my $pdb = shift; my $fh = shift; # Input file handle my $i; print "Resources:\n"; # Read each resource in turn for ($i = 0; $i < $pdb->{_numrecs}; $i++) { my $len; # Length of record my $buf; # Input buffer print " Resource $i\n"; # Sanity check: make sure we're where we think we # should be. if (tell($fh) > $pdb->{_index}[$i]{offset}) { die "Bad offset for resource $i: expected ", sprintf("0x%08x", $pdb->{_index}[$i]{offset}), " but it's at ", sprintf("0x%08x", tell($fh)), "\n"; } # Seek to the right place, if necessary if (tell($fh) != $pdb->{_index}[$i]{offset}) { seek PDB, $pdb->{_index}[$i]{offset}, 0; } # Compute the length of the resource: the last # resource extends to the end of the file. The others # extend to the beginning of the next resource. if ($i == $pdb->{_numrecs} - 1) { # This is the last resource $len = $pdb->{_size} - $pdb->{_index}[$i]{offset}; } else { # This is not the last resource $len = $pdb->{_index}[$i+1]{offset} - $pdb->{_index}[$i]{offset}; } # Read the resource read $fh, $buf, $len; if ($hexdump) { &hexdump(" ", $buf); print "\n"; } # Tell the real class to parse the resource data. Pass # &ParseResource all of the information from the # index, plus a "data" field with the raw resource # data. my $resource; $resource = $pdb->ParseResource( %{$pdb->{_index}[$i]}, "data" => $buf, ); push @{$pdb->{resources}}, $resource; # Print out the parsed values &dumphash($resource, "\t"); print "\n"; } } sub hexdump { my $prefix = shift; # What to print in front of each line my $data = shift; # The data to dump my $maxlines = shift; # Max # of lines to dump my $offset; # Offset of current chunk for ($offset = 0; $offset < length($data); $offset += 16) { my $hex; # Hex values of the data my $ascii; # ASCII values of the data my $chunk; # Current chunk of data last if defined($maxlines) && ($offset >= ($maxlines * 16)); $chunk = substr($data, $offset, 16); ($hex = $chunk) =~ s/./sprintf "%02x ", ord($&)/ges; ($ascii = $chunk) =~ y/\040-\176/./c; printf "%s %-48s|%-16s|\n", $prefix, $hex, $ascii; } } # XXX - Ought to have a &dumparray as well. The two can call each other # recursively. sub dumphash { my $hash = shift; my $indent = shift; my $key; my $value; while (($key, $value) = each %{$hash}) { if (ref($value) eq "HASH") { print $indent, $key, ":\n"; &dumphash($value, $indent . "\t"); } elsif (ref($value) eq "ARRAY") { my($i,$j); print $indent, $key, ":\n"; for ($i = 0; $i <= $#{$value}; $i++) { if (ref($value->[$i]) eq "HASH") { print $indent, " $i:\n"; &dumphash($value->[$i], $indent . "\t"); } elsif (ref($value->[$i]) eq "ARRAY") { my @v2 = @{$value->[$i]}; for ($j = 0; $j <= $#v2; $j++) { print $indent, "\t$i-$j: [$v2[$j]]\n"; } }else { print $indent, "\t$i: [$value->[$i]]\n"; } } } else { print $indent, $key, " -> [", $value, "]\n"; } } } __END__ =head1 NAME pdbdump - Print the contents of a Palm PDB file =head1 VERSION This document describes version 1.400 of main, released March 7, 2015 as part of Palm-PDB version 1.400. =head1 SYNOPSIS C I<[options]> F =head1 DESCRIPTION C reads a PalmOS F<.pdb> file, parses it, and prints its contents. This includes both a hex dump of the raw data of each piece, and a human-readable list of the various values, insofar as possible. The aim of C is to allow one to verify whether a particular file is a well-formed PalmOS database file and if not, where the error lies. If the database is of a known type, C parses the AppInfo block and records. Otherwise, it simply prints out a hex dump of their contents. C includes, by default, support for most of the built-in applications. Other helper modules may be loaded with the C<-M> option. =head1 OPTIONS =over 4 =item -h -help --help Print a usage message and exit. =item -nohex Don't print the hex dump of the various parts. =item -MI C the named module. This can be useful for loading additional helper modules. =back =head1 SEE ALSO L =head1 CONFIGURATION AND ENVIRONMENT main requires no configuration files or environment variables. =head1 INCOMPATIBILITIES None reported. =head1 BUGS AND LIMITATIONS C only recognizes record databases (C<.pdb> files), not resource databases (C<.prc> files). =head1 AUTHORS Andrew Arensburger C<< >> Currently maintained by Christopher J. Madsen C<< >> Please report any bugs or feature requests to S >>> or through the web interface at L<< http://rt.cpan.org/Public/Bug/Report.html?Queue=Palm-PDB >>. You can follow or contribute to Palm-PDB's development at L<< https://github.com/madsen/Palm-PDB >>. =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Andrew Arensburger. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =head1 DISCLAIMER OF WARRANTY BECAUSE THIS SOFTWARE IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE SOFTWARE, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE SOFTWARE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE SOFTWARE IS WITH YOU. SHOULD THE SOFTWARE PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR CORRECTION. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE SOFTWARE AS PERMITTED BY THE ABOVE LICENSE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE SOFTWARE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE SOFTWARE TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. =cut Palm-PDB-1.400/examples/copydb0000755000175000017500000000034712476641116014156 0ustar cjmcjm#!/usr/bin/env perl # Utility to copy the contents of a PDB. # $Id: copydb,v 1.2 1999/11/18 05:21:01 arensb Exp $ use strict; use Palm::PDB; use Palm::Raw; my $pdb = new Palm::PDB; $pdb->Load($ARGV[0]); $pdb->Write("foo.pdb"); Palm-PDB-1.400/Makefile.PL0000644000175000017500000000210112476641116013072 0ustar cjmcjm # This file was automatically generated by Dist::Zilla::Plugin::MakeMaker v5.029. use strict; use warnings; use 5.006; use ExtUtils::MakeMaker; my %WriteMakefileArgs = ( "ABSTRACT" => "Parse Palm database files", "AUTHOR" => "Christopher J. Madsen ", "CONFIGURE_REQUIRES" => { "ExtUtils::MakeMaker" => 0 }, "DISTNAME" => "Palm-PDB", "EXE_FILES" => [], "LICENSE" => "perl", "MIN_PERL_VERSION" => "5.006", "NAME" => "Palm::PDB", "PREREQ_PM" => { "constant" => "1.03" }, "TEST_REQUIRES" => { "Test::More" => 0 }, "VERSION" => "1.400", "test" => { "TESTS" => "t/*.t" } ); my %FallbackPrereqs = ( "ExtUtils::MakeMaker" => 0, "Test::More" => 0, "constant" => "1.03" ); unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) { delete $WriteMakefileArgs{TEST_REQUIRES}; delete $WriteMakefileArgs{BUILD_REQUIRES}; $WriteMakefileArgs{PREREQ_PM} = \%FallbackPrereqs; } delete $WriteMakefileArgs{CONFIGURE_REQUIRES} unless eval { ExtUtils::MakeMaker->VERSION(6.52) }; WriteMakefile(%WriteMakefileArgs); Palm-PDB-1.400/t/0000755000175000017500000000000012476641116011371 5ustar cjmcjmPalm-PDB-1.400/t/00-all_prereqs.t0000644000175000017500000000533112476641116014306 0ustar cjmcjm#!perl use strict; use warnings; # This doesn't use Test::More because I don't want to clutter %INC # with modules that aren't prerequisites. my $test = 0; my $tests_completed; sub ok ($$) { my ($ok, $name) = @_; printf "%sok %d - %s\n", ($ok ? '' : 'not '), ++$test, $name; return $ok; } # end ok END { ok(0, 'unknown failure') unless defined $tests_completed; print "1..$tests_completed\n"; } sub get_version { my ($package) = @_; local $@; my $version = eval { $package->VERSION }; defined $version ? $version : 'undef'; } # end get_version TEST: { ok(open(META, ') { last if /^\s*"prereqs" : \{\s*\z/; } # end while ok(defined $_, 'found prereqs') or last TEST; while () { last if /^\s*\},?\s*\z/; ok(/^\s*"(.+)" : \{\s*\z/, "found phase $1") or last TEST; my $phase = $1; while () { last if /^\s*\},?\s*\z/; next if /^\s*"[^"]+"\s*:\s*\{\s*\},?\s*\z/; ok(/^\s*"(.+)" : \{\s*\z/, "found relationship $phase $1") or last TEST; my $rel = $1; while () { last if /^\s*\},?\s*\z/; ok(/^\s*"([^"]+)"\s*:\s*(\S+?),?\s*\z/, "found prereq $1") or last TEST; my ($prereq, $version) = ($1, $2); next if $phase ne 'runtime' or $prereq eq 'perl'; # Need a special case for if.pm, because "require if;" is a syntax error. my $loaded = ($prereq eq 'if') ? eval "require '$prereq.pm'; 1" : eval "require $prereq; 1"; if ($rel eq 'requires') { ok($loaded, "loaded $prereq") or print STDERR "\n# ERROR: Wanted: $prereq $version\n"; } else { ok(1, ($loaded ? 'loaded' : 'failed to load') . " $prereq"); } if ($loaded and not ($version eq '"0"' or eval "'$prereq'->VERSION($version); 1")) { printf STDERR "\n# WARNING: Got: %s %s\n# Wanted: %s %s\n", $prereq, get_version($prereq), $prereq, $version; } } # end while in prerequisites } # end while in relationship } # end while in phase close META; # Print version of all loaded modules: if ($ENV{AUTOMATED_TESTING} or (@ARGV and ($ARGV[0] eq '-v' or $ARGV[0] eq '--verbose'))) { print STDERR "# Listing %INC\n"; my @packages = grep { s/\.pm\Z// and do { s![\\/]!::!g; 1 } } sort keys %INC; my $len = 0; for (@packages) { $len = length if length > $len } $len = 68 if $len > 68; for my $package (@packages) { printf STDERR "# %${len}s %s\n", $package, get_version($package); } } # end if AUTOMATED_TESTING } # end TEST $tests_completed = $test; Palm-PDB-1.400/t/bogus1DB.pdb0000644000175000017500000000250612476641116013471 0ustar cjmcjmBogus1..........................´:i/¼v¶v¼v¶v`DATAaddrÞÎUnfiledBusinessPersonalQuickList ?ÿÿLast nameFirst nameCompanyWorkHomeFaxOtherE-mailAddressCityStateZip CodeCountryTitleCustom 1Custom 2Custom 3Custom 4NoteMainPagerMobile5U½ AccessoriesPalm, Inc.800-881-7256 (US)800-891-6342 (Canada)Int'l: See Worldwide Customer Support Information CardWebsite: www.palm.comTo find accessories and useful add-on software, please visit our web site at www.palm.com.2UÝTechnical SupportPalm, Inc.847-262-PALM (7256) (US)Int'l: See Worldwide Customer Support Information CardWebsite: www.palm.comsupport@palm.comFor technical support outside of the US, please use the numbers listed on the Worldwide Customer Support Information card. For the latest information on products and upgrades, check our web site regularly at www.palm.com.Palm-PDB-1.400/t/bogus2DB.pdb0000644000175000017500000000011012476641116013457 0ustar cjmcjmbogus2DB´:i/¼v¶v¼v¶v`DATAaddrPalm-PDB-1.400/t/loadwrite.t0000755000175000017500000000120712476641116013553 0ustar cjmcjmuse strict; use Test::More qw(no_plan); BEGIN { use_ok('Palm::PDB') } BEGIN { use_ok('Palm::Raw') } BEGIN { use_ok('IO::File') } my $name = "t/Test.pdb"; my $pdb = Palm::Raw->new; ok( defined $pdb ); $pdb->{name} = "Test"; for( qw(tic tac toe) ) { print "storing '$_'\n"; my $rec = $pdb->append_Record(); ok( defined $rec ); $rec->{data} = $_; } $pdb->Write( $name ); pass( "Write" ); undef $pdb; my $fh = new IO::File $name, "r+"; ok( defined $fh ); $pdb = Palm::PDB->new; ok( defined $pdb ); $pdb->Load( $fh ); pass( "Load" ); for( @{$pdb->{records}} ) { print "got '$_->{data}'\n"; ok( defined $_ and length $_->{data} > 0 ); } 1; Palm-PDB-1.400/t/00-load.t0000644000175000017500000000034112476641116012710 0ustar cjmcjm#! /usr/bin/perl #--------------------------------------------------------------------- use Test::More tests => 2; BEGIN { use_ok('Palm::PDB'); use_ok('Palm::Raw'); } diag("Testing Palm::PDB $Palm::PDB::VERSION"); Palm-PDB-1.400/t/bogusdb.t0000755000175000017500000000215012476641116013204 0ustar cjmcjm# Before `make install' is performed this script should be runnable with # `make test'. After `make install' it should work as `perl test.pl' ######################### We start with some black magic to print on failure. # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) BEGIN { $| = 1; print "1..4\n"; } END {print "not ok 1\n" unless $loaded;} use Palm::PDB; $loaded = 1; print "ok 1\n"; use Palm::Raw; ######################### End of black magic. # Insert your test code below (better if it prints "ok 13" # (correspondingly "not ok 13") depending on the success of chunk 13 # of the test code): eval { my $pdb = Palm::PDB->new; $pdb->Load( 't/bogusdb.t' ); }; unless( $@ ) { print "not ok 2\n"; } else { print "ok 2\n"; } eval { # name doesn't have a NUL my $pdb = Palm::PDB->new; $pdb->Load( 't/bogus1DB.pdb' ); }; unless( $@ ) { print "not ok 3\n"; } else { print "ok 3\n"; } eval { # truncated after header my $pdb = Palm::PDB->new; $pdb->Load( 't/bogus2DB.pdb' ); }; unless( $@ ) { print "not ok 4\n"; } else { print "ok 4\n"; } 1; Palm-PDB-1.400/META.json0000644000175000017500000002365312476641116012560 0ustar cjmcjm{ "abstract" : "Parse Palm database files", "author" : [ "Christopher J. Madsen " ], "dynamic_config" : 0, "generated_by" : "Dist::Zilla version 5.029, CPAN::Meta::Converter version 2.142690", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : "2" }, "name" : "Palm-PDB", "prereqs" : { "configure" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "develop" : { "requires" : { "Pod::Coverage::TrustPod" : "0", "Test::Pod" : "1.41", "Test::Pod::Coverage" : "1.08" } }, "runtime" : { "requires" : { "constant" : "1.03", "perl" : "5.006" } }, "test" : { "requires" : { "Test::More" : "0" } } }, "release_status" : "stable", "resources" : { "repository" : { "type" : "git", "url" : "git://github.com/madsen/Palm-PDB.git", "web" : "https://github.com/madsen/Palm-PDB" } }, "version" : "1.400", "x_Dist_Zilla" : { "perl" : { "version" : "5.018002" }, "plugins" : [ { "class" : "Dist::Zilla::Plugin::OurPkgVersion", "name" : "OurPkgVersion", "version" : "0.005001" }, { "class" : "Dist::Zilla::Plugin::VersionFromModule", "name" : "CJM/VersionFromModule", "version" : "0.08" }, { "class" : "Dist::Zilla::Plugin::GatherDir", "config" : { "Dist::Zilla::Plugin::GatherDir" : { "exclude_filename" : [], "exclude_match" : [], "follow_symlinks" : "0", "include_dotfiles" : "0", "prefix" : "", "prune_directory" : [], "root" : "." } }, "name" : "CJM/GatherDir", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::PruneCruft", "name" : "CJM/PruneCruft", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::ManifestSkip", "name" : "CJM/ManifestSkip", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::MetaJSON", "name" : "CJM/MetaJSON", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::MetaYAML", "name" : "CJM/MetaYAML", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::License", "name" : "CJM/License", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::Test::PrereqsFromMeta", "name" : "CJM/Test::PrereqsFromMeta", "version" : "4.23" }, { "class" : "Dist::Zilla::Plugin::PodSyntaxTests", "name" : "CJM/PodSyntaxTests", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::PodCoverageTests", "name" : "CJM/PodCoverageTests", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::PodLoom", "config" : { "Pod::Loom version" : "0.08" }, "name" : "CJM/PodLoom", "version" : "5.00" }, { "class" : "Dist::Zilla::Plugin::MakeMaker", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 1 } }, "name" : "CJM/MakeMaker", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::RunExtraTests", "config" : { "Dist::Zilla::Role::TestRunner" : { "default_jobs" : 1 } }, "name" : "CJM/RunExtraTests", "version" : "0.011" }, { "class" : "Dist::Zilla::Plugin::MetaConfig", "name" : "CJM/MetaConfig", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::MatchManifest", "name" : "CJM/MatchManifest", "version" : "4.02" }, { "class" : "Dist::Zilla::Plugin::RecommendedPrereqs", "name" : "CJM/RecommendedPrereqs", "version" : "4.21" }, { "class" : "Dist::Zilla::Plugin::CheckPrereqsIndexed", "name" : "CJM/CheckPrereqsIndexed", "version" : "0.009" }, { "class" : "Dist::Zilla::Plugin::GitVersionCheckCJM", "name" : "CJM/GitVersionCheckCJM", "version" : "4.26" }, { "class" : "Dist::Zilla::Plugin::TemplateCJM", "name" : "CJM/TemplateCJM", "version" : "4.22" }, { "class" : "Dist::Zilla::Plugin::Repository", "name" : "CJM/Repository", "version" : "0.19" }, { "class" : "Dist::Zilla::Plugin::Git::Check", "config" : { "Dist::Zilla::Plugin::Git::Check" : { "untracked_files" : "die" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Changes" ], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "repo_root" : "." } }, "name" : "CJM/@Git/Check", "version" : "2.030" }, { "class" : "Dist::Zilla::Plugin::Git::Commit", "config" : { "Dist::Zilla::Plugin::Git::Commit" : { "add_files_in" : [], "commit_msg" : "Updated Changes for %{MMMM d, yyyy}d%{ trial}t release of %v", "time_zone" : "local" }, "Dist::Zilla::Role::Git::DirtyFiles" : { "allow_dirty" : [ "Changes" ], "allow_dirty_match" : [], "changelog" : "Changes" }, "Dist::Zilla::Role::Git::Repo" : { "repo_root" : "." } }, "name" : "CJM/@Git/Commit", "version" : "2.030" }, { "class" : "Dist::Zilla::Plugin::Git::Tag", "config" : { "Dist::Zilla::Plugin::Git::Tag" : { "branch" : null, "signed" : 0, "tag" : "1.400", "tag_format" : "%v%t", "tag_message" : "Tagged %N %v%{ (trial release)}t", "time_zone" : "local" }, "Dist::Zilla::Role::Git::Repo" : { "repo_root" : "." } }, "name" : "CJM/@Git/Tag", "version" : "2.030" }, { "class" : "Dist::Zilla::Plugin::Git::Push", "config" : { "Dist::Zilla::Plugin::Git::Push" : { "push_to" : [ "github master" ], "remotes_must_exist" : 1 }, "Dist::Zilla::Role::Git::Repo" : { "repo_root" : "." } }, "name" : "CJM/@Git/Push", "version" : "2.030" }, { "class" : "Dist::Zilla::Plugin::TestRelease", "name" : "CJM/TestRelease", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::UploadToCPAN", "name" : "CJM/UploadToCPAN", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::ArchiveRelease", "name" : "CJM/ArchiveRelease", "version" : "4.26" }, { "class" : "Dist::Zilla::Plugin::AutoPrereqs", "name" : "AutoPrereqs", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::Encoding", "name" : "Encoding", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::FileFinder::ByName", "name" : ":TestFiles", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::FileFinder::ByName", "name" : ":PodExamples", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":InstallModules", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":IncModules", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ExecFiles", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":ShareFiles", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":MainModule", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":AllFiles", "version" : "5.029" }, { "class" : "Dist::Zilla::Plugin::FinderCode", "name" : ":NoFiles", "version" : "5.029" } ], "zilla" : { "class" : "Dist::Zilla::Dist::Builder", "config" : { "is_trial" : "0" }, "version" : "5.029" } } } Palm-PDB-1.400/META.yml0000644000175000017500000001417112476641116012403 0ustar cjmcjm--- abstract: 'Parse Palm database files' author: - 'Christopher J. Madsen ' build_requires: Test::More: 0 configure_requires: ExtUtils::MakeMaker: 0 dynamic_config: 0 generated_by: 'Dist::Zilla version 5.029, CPAN::Meta::Converter version 2.142690' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: 1.4 name: Palm-PDB requires: constant: 1.03 perl: 5.006 resources: repository: git://github.com/madsen/Palm-PDB.git version: 1.400 x_Dist_Zilla: perl: version: 5.018002 plugins: - class: Dist::Zilla::Plugin::OurPkgVersion name: OurPkgVersion version: 0.005001 - class: Dist::Zilla::Plugin::VersionFromModule name: CJM/VersionFromModule version: 0.08 - class: Dist::Zilla::Plugin::GatherDir config: Dist::Zilla::Plugin::GatherDir: exclude_filename: [] exclude_match: [] follow_symlinks: 0 include_dotfiles: 0 prefix: '' prune_directory: [] root: '.' name: CJM/GatherDir version: 5.029 - class: Dist::Zilla::Plugin::PruneCruft name: CJM/PruneCruft version: 5.029 - class: Dist::Zilla::Plugin::ManifestSkip name: CJM/ManifestSkip version: 5.029 - class: Dist::Zilla::Plugin::MetaJSON name: CJM/MetaJSON version: 5.029 - class: Dist::Zilla::Plugin::MetaYAML name: CJM/MetaYAML version: 5.029 - class: Dist::Zilla::Plugin::License name: CJM/License version: 5.029 - class: Dist::Zilla::Plugin::Test::PrereqsFromMeta name: CJM/Test::PrereqsFromMeta version: 4.23 - class: Dist::Zilla::Plugin::PodSyntaxTests name: CJM/PodSyntaxTests version: 5.029 - class: Dist::Zilla::Plugin::PodCoverageTests name: CJM/PodCoverageTests version: 5.029 - class: Dist::Zilla::Plugin::PodLoom config: Pod::Loom version: 0.08 name: CJM/PodLoom version: 5.00 - class: Dist::Zilla::Plugin::MakeMaker config: Dist::Zilla::Role::TestRunner: default_jobs: 1 name: CJM/MakeMaker version: 5.029 - class: Dist::Zilla::Plugin::RunExtraTests config: Dist::Zilla::Role::TestRunner: default_jobs: 1 name: CJM/RunExtraTests version: 0.011 - class: Dist::Zilla::Plugin::MetaConfig name: CJM/MetaConfig version: 5.029 - class: Dist::Zilla::Plugin::MatchManifest name: CJM/MatchManifest version: 4.02 - class: Dist::Zilla::Plugin::RecommendedPrereqs name: CJM/RecommendedPrereqs version: 4.21 - class: Dist::Zilla::Plugin::CheckPrereqsIndexed name: CJM/CheckPrereqsIndexed version: 0.009 - class: Dist::Zilla::Plugin::GitVersionCheckCJM name: CJM/GitVersionCheckCJM version: 4.26 - class: Dist::Zilla::Plugin::TemplateCJM name: CJM/TemplateCJM version: 4.22 - class: Dist::Zilla::Plugin::Repository name: CJM/Repository version: 0.19 - class: Dist::Zilla::Plugin::Git::Check config: Dist::Zilla::Plugin::Git::Check: untracked_files: die Dist::Zilla::Role::Git::DirtyFiles: allow_dirty: - Changes allow_dirty_match: [] changelog: Changes Dist::Zilla::Role::Git::Repo: repo_root: '.' name: CJM/@Git/Check version: 2.030 - class: Dist::Zilla::Plugin::Git::Commit config: Dist::Zilla::Plugin::Git::Commit: add_files_in: [] commit_msg: 'Updated Changes for %{MMMM d, yyyy}d%{ trial}t release of %v' time_zone: local Dist::Zilla::Role::Git::DirtyFiles: allow_dirty: - Changes allow_dirty_match: [] changelog: Changes Dist::Zilla::Role::Git::Repo: repo_root: '.' name: CJM/@Git/Commit version: 2.030 - class: Dist::Zilla::Plugin::Git::Tag config: Dist::Zilla::Plugin::Git::Tag: branch: ~ signed: 0 tag: 1.400 tag_format: '%v%t' tag_message: 'Tagged %N %v%{ (trial release)}t' time_zone: local Dist::Zilla::Role::Git::Repo: repo_root: '.' name: CJM/@Git/Tag version: 2.030 - class: Dist::Zilla::Plugin::Git::Push config: Dist::Zilla::Plugin::Git::Push: push_to: - 'github master' remotes_must_exist: 1 Dist::Zilla::Role::Git::Repo: repo_root: '.' name: CJM/@Git/Push version: 2.030 - class: Dist::Zilla::Plugin::TestRelease name: CJM/TestRelease version: 5.029 - class: Dist::Zilla::Plugin::UploadToCPAN name: CJM/UploadToCPAN version: 5.029 - class: Dist::Zilla::Plugin::ArchiveRelease name: CJM/ArchiveRelease version: 4.26 - class: Dist::Zilla::Plugin::AutoPrereqs name: AutoPrereqs version: 5.029 - class: Dist::Zilla::Plugin::Encoding name: Encoding version: 5.029 - class: Dist::Zilla::Plugin::FileFinder::ByName name: ':TestFiles' version: 5.029 - class: Dist::Zilla::Plugin::FileFinder::ByName name: ':PodExamples' version: 5.029 - class: Dist::Zilla::Plugin::FinderCode name: ':InstallModules' version: 5.029 - class: Dist::Zilla::Plugin::FinderCode name: ':IncModules' version: 5.029 - class: Dist::Zilla::Plugin::FinderCode name: ':ExecFiles' version: 5.029 - class: Dist::Zilla::Plugin::FinderCode name: ':ShareFiles' version: 5.029 - class: Dist::Zilla::Plugin::FinderCode name: ':MainModule' version: 5.029 - class: Dist::Zilla::Plugin::FinderCode name: ':AllFiles' version: 5.029 - class: Dist::Zilla::Plugin::FinderCode name: ':NoFiles' version: 5.029 zilla: class: Dist::Zilla::Dist::Builder config: is_trial: 0 version: 5.029 Palm-PDB-1.400/MANIFEST0000644000175000017500000000041512476641116012257 0ustar cjmcjmChanges LICENSE MANIFEST META.json META.yml Makefile.PL README examples/copydb examples/pdbdump-raw lib/Palm/PDB.pm lib/Palm/Raw.pm t/00-all_prereqs.t t/00-load.t t/bogus1DB.pdb t/bogus2DB.pdb t/bogusdb.t t/loadwrite.t xt/release/pod-coverage.t xt/release/pod-syntax.t Palm-PDB-1.400/LICENSE0000644000175000017500000004367512476641116012152 0ustar cjmcjmThis software is copyright (c) 2000 by Andrew Arensburger. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. Terms of the Perl programming language system itself a) the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version, or b) the "Artistic License" --- The GNU General Public License, Version 1, February 1989 --- This software is Copyright (c) 2000 by Andrew Arensburger. This is free software, licensed under: The GNU General Public License, Version 1, February 1989 GNU GENERAL PUBLIC LICENSE Version 1, February 1989 Copyright (C) 1989 Free Software Foundation, Inc. 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The license agreements of most software companies try to keep users at the mercy of those companies. By contrast, our General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. The General Public License applies to the Free Software Foundation's software and to any other program whose authors commit to using it. You can use it for your programs, too. When we speak of free software, we are referring to freedom, not price. Specifically, the General Public License is designed to make sure that you have the freedom to give away or sell copies of free software, that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of a such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must tell them their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any work containing the Program or a portion of it, either verbatim or with modifications. Each licensee is addressed as "you". 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this General Public License and to the absence of any warranty; and give any other recipients of the Program a copy of this General Public License along with the Program. You may charge a fee for the physical act of transferring a copy. 2. You may modify your copy or copies of the Program or any portion of it, and copy and distribute such modifications under the terms of Paragraph 1 above, provided that you also do the following: a) cause the modified files to carry prominent notices stating that you changed the files and the date of any change; and b) cause the whole of any work that you distribute or publish, that in whole or in part contains the Program or any part thereof, either with or without modifications, to be licensed at no charge to all third parties under the terms of this General Public License (except that you may choose to grant warranty protection to some or all third parties, at your option). c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the simplest and most usual way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this General Public License. d) You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. Mere aggregation of another independent work with the Program (or its derivative) on a volume of a storage or distribution medium does not bring the other work under the scope of these terms. 3. You may copy and distribute the Program (or a portion or derivative of it, under Paragraph 2) in object code or executable form under the terms of Paragraphs 1 and 2 above provided that you also do one of the following: a) accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Paragraphs 1 and 2 above; or, b) accompany it with a written offer, valid for at least three years, to give any third party free (except for a nominal charge for the cost of distribution) a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Paragraphs 1 and 2 above; or, c) accompany it with the information you received as to where the corresponding source code may be obtained. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form alone.) Source code for a work means the preferred form of the work for making modifications to it. For an executable file, complete source code means all the source code for all modules it contains; but, as a special exception, it need not include source code for modules which are standard libraries that accompany the operating system on which the executable file runs, or for standard header files or definitions files that accompany that operating system. 4. You may not copy, modify, sublicense, distribute or transfer the Program except as expressly provided under this General Public License. Any attempt otherwise to copy, modify, sublicense, distribute or transfer the Program is void, and will automatically terminate your rights to use the Program under this License. However, parties who have received copies, or rights to use copies, from you under this General Public License will not have their licenses terminated so long as such parties remain in full compliance. 5. By copying, distributing or modifying the Program (or any work based on the Program) you indicate your acceptance of this license to do so, and all its terms and conditions. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. 7. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of the license which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the license, you may choose any version ever published by the Free Software Foundation. 8. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to humanity, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19xx name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (a program to direct compilers to make passes at assemblers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice That's all there is to it! --- The Artistic License 1.0 --- This software is Copyright (c) 2000 by Andrew Arensburger. This is free software, licensed under: The Artistic License 1.0 The Artistic License Preamble The intent of this document is to state the conditions under which a Package may be copied, such that the Copyright Holder maintains some semblance of artistic control over the development of the package, while giving the users of the package the right to use and distribute the Package in a more-or-less customary fashion, plus the right to make reasonable modifications. Definitions: - "Package" refers to the collection of files distributed by the Copyright Holder, and derivatives of that collection of files created through textual modification. - "Standard Version" refers to such a Package if it has not been modified, or has been modified in accordance with the wishes of the Copyright Holder. - "Copyright Holder" is whoever is named in the copyright or copyrights for the package. - "You" is you, if you're thinking about copying or distributing this Package. - "Reasonable copying fee" is whatever you can justify on the basis of media cost, duplication charges, time of people involved, and so on. (You will not be required to justify it to the Copyright Holder, but only to the computing community at large as a market that must bear the fee.) - "Freely Available" means that no fee is charged for the item itself, though there may be fees involved in handling the item. It also means that recipients of the item may redistribute it under the same conditions they received it. 1. You may make and give away verbatim copies of the source form of the Standard Version of this Package without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may apply bug fixes, portability fixes and other modifications derived from the Public Domain or from the Copyright Holder. A Package modified in such a way shall still be considered the Standard Version. 3. You may otherwise modify your copy of this Package in any way, provided that you insert a prominent notice in each changed file stating how and when you changed that file, and provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or placing the modifications on a major archive site such as ftp.uu.net, or by allowing the Copyright Holder to include your modifications in the Standard Version of the Package. b) use the modified Package only within your corporation or organization. c) rename any non-standard executables so the names do not conflict with standard executables, which must also be provided, and provide a separate manual page for each non-standard executable that clearly documents how it differs from the Standard Version. d) make other distribution arrangements with the Copyright Holder. 4. You may distribute the programs of this Package in object code or executable form, provided that you do at least ONE of the following: a) distribute a Standard Version of the executables and library files, together with instructions (in the manual page or equivalent) on where to get the Standard Version. b) accompany the distribution with the machine-readable source of the Package with your modifications. c) accompany any non-standard executables with their corresponding Standard Version executables, giving the non-standard executables non-standard names, and clearly documenting the differences in manual pages (or equivalent), together with instructions on where to get the Standard Version. d) make other distribution arrangements with the Copyright Holder. 5. You may charge a reasonable copying fee for any distribution of this Package. You may charge any fee you choose for support of this Package. You may not charge a fee for this Package itself. However, you may distribute this Package in aggregate with other (possibly commercial) programs as part of a larger (possibly commercial) software distribution provided that you do not advertise this Package as a product of your own. 6. The scripts and library files supplied as input to or produced as output from the programs of this Package do not automatically fall under the copyright of this Package, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this Package. 7. C or perl subroutines supplied by you and linked into this Package shall not be considered part of this Package. 8. The name of the Copyright Holder may not be used to endorse or promote products derived from this software without specific prior written permission. 9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. The End Palm-PDB-1.400/Changes0000644000175000017500000005337712476641116012440 0ustar cjmcjmRevision history for Palm-PDB 1.400 2015-03-07 Release by Christopher J. Madsen - Bump version number to be greater than all previous releases. Palm::PDB 1.36 was released in p5-Palm 1.008, but p5-Palm 1.009 contained Palm::PDB 1.009. Version numbers shouldn't go backwards. (reported by David Cantrell - GitHub#1) 1.016 2015-01-24 Release by Christopher J. Madsen - Move deleted records to end of file (Alan Wehmann - RT#101666) - Use 'our' instead of 'use vars' 1.015 2014-08-09 Release by Christopher J. Madsen - Minor documentation improvements 1.014 2014-07-26 Release by Christopher J. Madsen - New maintainer Christopher J. Madsen - Palm::PDB & Palm::Raw are now distributed separately from the other Palm modules - With permission from Andrew Arensburger & Alessandro Zummo Palm-PDB is now distributed under the same terms as Perl 5 - Constant variables in Palm::PDB are now proper constants 1.013 2013-08-21 Release by brian d foy - Add notice that this distribution is in need of a new maintainer 1.012 2010-02-23 Release by brian d foy - Promote to a full release from the latest development cycle 1.011_01 2010-01-02 Trial Release by brian d foy - Palm/Datebook.pm: add support for time zone entries (Tim Adye ) 1.011 2009-08-17 Release by brian d foy - Palm/Datebook.pm: add support for the newer-format Palm Calendar databases (Tim Adye ) 1.009 2007-07-30 Release by brian d foy - cleaned up the pod coverage test. - add License and Author stuff to the distro 1.006 2007-01-15 Release by brian d foy - chromatic sent me some changes living in another distro (p5-Palm-1.004_000). It catches up 1.003_000 for all of the changes since November 2002 listed in this file. 1.005 2007-01-13 Release by brian d foy - Cleansed the dist to make it installable by CPAN tools. It just needs a version without a _ in it. (http://rt.cpan.org/Ticket/Display.html?id=12837) 1.004 2004-04-15 Various Authors The release history before version 1.006 is muddled. These are the changes listed, but it's not clear what was released when. 2004-04-15 21:21 christophe - Palm/PDB.pm, util/pdbdump: Added some logic to detect broken databases during loading: - database names _must_ contain the terminating NUL within the first 32 characters of the header. This catches obviously broken situations like text files renamed to .pdb files (which, oddly enough, happens a lot when you're trying to process text files into Doc files). - position offsets mustn't go beyond the end of the database file ($pdb->{_size}). This indicates either truncation of the db or straight corruption. 2004-04-08 22:55 christophe - Palm/PDB.pm: make it impossible to create a nameless database. It can't be uploaded to the PDA, that's for sure. 2003-11-09 20:40 azummo - Palm/PDB.pm: Handle uniqueID and uniqueIDseed in the Palm OS way. 2003-11-05 15:19 azummo - Palm/DateTime.pm: Fixed ISO8601 format in palmtime_to_iso8601 Minor stylistical changes 2003-10-10 19:19 azummo - Palm/Datebook.pm: Fixed alarm documentation (Christophe Beauregard) 2003-10-10 07:01 azummo - Palm/PDB.pm: Implemented remove_Record() method. Fixed dirty flags handling. (Christophe Beauregard) 2003-10-10 06:51 azummo - Palm/PDB.pm: Added is_Dirty method (Christophe Beauregard). 2003-09-16 18:59 azummo - MANIFEST, README: Updated in preparation for the new release. 2003-09-16 18:56 azummo - Makefile.PL: Bumped version number. 2003-09-16 18:53 azummo - Palm/ZirePhoto.pm: Removed unused methods. The superclass will take care. 2003-08-03 13:06 arensb - README, Palm/PDB.pm: Added missing section header in POD. Patch contributed by Kurt Starsinic 2003-06-26 15:21 azummo - Palm/Mail.pm: Some headers were left undefined (Christophe Beauregard) 2003-06-24 10:28 azummo - Palm/ZirePhoto.pm: Added. 2002-12-17 19:09 azummo - MANIFEST: Added DateTime.pm to manifest. 2002-11-07 10:50 arensb - Makefile.PL: Updated version number. 2002-11-07 09:27 arensb - Palm/StdAppInfo.pm: (API) Now supports databases where the last byte of the AppInfo block is data instead of padding. Backwards-compatible. 2002-11-07 09:11 arensb - Palm/: Address.pm, Datebook.pm, Mail.pm, Memo.pm, ToDo.pm: Rearranged @ISA, so that it'll get the right &ParseAppInfoBlock. 2002-11-03 11:43 azummo - Palm/: Address.pm, DateTime.pm, Datebook.pm, Mail.pm, Memo.pm, PDB.pm, PQA.pm, Raw.pm, StdAppInfo.pm, ToDo.pm: Changed $VERSION parser. 2002-09-03 13:48 azummo - Palm/Address.pm: Patch from Brian Johnson (it just adds an else to the if statement to nullify non-existing entities). 2002-06-22 09:16 azummo - Palm/PDB.pm: now now takes an optional hash with default values. 2002-06-16 09:37 azummo - Palm/PDB.pm: Added some compatibility code. Reflected (part of) changes in the POD. 2002-06-16 09:26 azummo - Palm/PDB.pm: Make some more use of the new attribute names. 2002-06-16 08:34 azummo - Palm/PDB.pm: Added "official" names to record attributes. 2002-06-16 08:19 azummo - Palm/PDB.pm: Corrected an attribute flag (open) Added new attributes using "official" names 2002-05-09 11:08 arensb - Palm/Address.pm: Again, more sanity-checking and a little less code duplication. 2002-05-09 10:33 arensb - Palm/Address.pm: More sanity checks, and a somewhat more elegant way of doing things (iterate over field names, rather than duplicating code a bunch of times). 2002-05-09 10:24 arensb - Palm/StdAppInfo.pm: (bug fix) Used to do some sanity-checking, then ignored it. We now pay attention to our own sanity checks. 2002-04-09 20:59 arensb - README: Added Keni as a contributor. 2002-04-09 20:58 arensb - util/pdbdump: Applied Keni's patch: array-handling for parsed records (e.g., Datebook exceptions). 2002-04-05 14:59 azummo - Palm/DateTime.pm: Hopefully fixed mktime() invocation. (?) 2002-03-23 08:38 arensb - Palm/Mail.pm: Added extra sanity checks. 2002-03-12 15:23 azummo - Palm/DateTime.pm: Added Palm/DateTime.pm 2002-02-15 09:40 arensb - Palm/PDB.pm: (bug fix) Strip everything after the first NUL, not just trailing NULs. Patch submitted by Don Park 2002-02-08 09:20 arensb - Makefile.PL: Bumped up version number. 2002-02-08 08:56 arensb - MANIFEST: Added add-memo to distribution. 2002-02-08 08:55 arensb - util/add-memo: Added first draft of add-memo, to add a memo to an existing MemoDB.pdb. 2002-02-01 09:09 arensb - Palm/PDB.pm: Comment used to refer to a doc file in ColdSync that no longer exists. Fixed. 2002-01-26 17:25 arensb - Palm/Address.pm, Palm/Datebook.pm, Palm/Mail.pm, Palm/Memo.pm, Palm/PDB.pm, Palm/PQA.pm, Palm/Raw.pm, Palm/StdAppInfo.pm, Palm/ToDo.pm, util/pdbdump: CPAN-friendly $VERSION. 2002-01-26 17:22 arensb - TODO: Added some items. 2002-01-26 17:22 arensb - Makefile.PL: Rewrote version number in CPAN-friendly format. 2001-06-05 09:04 arensb - Palm/StdAppInfo.pm: Made addCategory, deleteCategory, renameCategory behave a bit more like the Palm, even when it's brain-damaged. 2001-06-05 08:52 arensb - TODO: Removed an item. 2001-06-02 15:17 arensb - Palm/: Datebook.pm, StdAppInfo.pm: Fixed typo in POD. 2001-04-04 23:54 arensb - Palm/StdAppInfo.pm: Fixed typo. 2001-03-30 01:23 arensb - Palm/Datebook.pm: Clarified whether date numbers start at 0 or 1. (bug fix): Fixed year conversion. (bug fix): Fixed use of undefined value. 2001-02-24 18:35 arensb - Palm/PDB.pm: In &new, initialize 'baktime' to -$EPOCH_1904, that being the Palm's epoch. 2001-02-24 17:59 arensb - FAQ: Added pointers to other sources of information about HotSync .dat files. 2001-02-24 11:49 arensb - Palm/Datebook.pm: Clarified documentation. 2001-02-20 06:17 arensb - MANIFEST: Added FAQ to distribution. 2001-02-20 06:17 arensb - Makefile.PL: Incremented version number. 2001-02-20 06:17 arensb - FAQ: (Added). 2001-02-20 06:12 arensb - README: Added contributor. 2001-02-20 06:12 arensb - util/pdbdump: (bug fix): the two NULs are optional. Fixed typo. 2001-02-20 06:11 arensb - Palm/StdAppInfo.pm: Added a "to do" comment. 2000-11-09 10:32 arensb - Palm/PDB.pm: Applied John-David Smith's patch: add the "archive" attribute. Better check for non-NULLness of AppInfo block. 2000-09-25 01:06 arensb - util/pdbdump: Added "launchable" attribute, for PQAs. Print database attributes more clearly. 2000-09-25 01:05 arensb - Palm/PQA.pm: First draft of PQA parser. 2000-09-25 01:05 arensb - Palm/PDB.pm: Added "launchable" attribute, for PQAs. 2000-09-24 12:37 arensb - util/pdbdump: Now handles resource databases as well as record databases. 2000-09-24 12:25 arensb - Palm/: Address.pm, Datebook.pm, Mail.pm, Memo.pm, PDB.pm, Raw.pm, StdAppInfo.pm, ToDo.pm: $VERSION is now a floating-point number, for comparison. 2000-09-24 12:24 arensb - TODO: Added an item. 2000-09-20 10:16 arensb - Palm/StdAppInfo.pm: Removed a "to do" comment. 2000-09-16 19:58 arensb - Makefile.PL: Bumped up patchlevel number. 2000-09-16 19:58 arensb - Palm/PDB.pm: (bug fix): The two useless NULs are allowed, but not mandated, by the spec. By assuming that they will always be there, this module failed to parse some well-formed PDBs. 2000-09-16 19:57 arensb - util/pdbdump: Oops! Forgot to fix resources. Minor cleaning. 2000-09-16 19:53 arensb - util/pdbdump: (bug fix): The two useless NULs are allowed, but not mandated, by the spec. By assuming that they will always be there, 'pdbdump' failed to parse some well-formed PDBs. 2000-09-09 00:52 arensb - Makefile.PL: Bumped up version number. 2000-09-09 00:50 arensb - Palm/StdAppInfo.pm: Added $error variable. (bug fix): A mis-written test would cause &pack_StdAppInfo to omit unnamed categories. Hence, the category list had fewer than 16 categories, when written. Fixed typo. (feature): Added &addCategory, &deleteCategory, &renameCategory. Removed BUGS section. 2000-09-08 22:48 arensb - Palm/StdAppInfo.pm: Stop Perl from complaining if there are unnamed categories. 2000-09-08 22:47 arensb - README: Added author. 2000-08-28 23:20 arensb - Makefile.PL: Bumped up the version number. 2000-08-28 23:19 arensb - Palm/StdAppInfo.pm: Changed API: the categories are new arranged as a 16-element array of category structs, the way they logically belong, rather than as disparate arrays, the way they're represented in the file. Added a lot to the POD. 2000-08-28 23:16 arensb - TODO: Removed an item. 2000-08-28 23:16 arensb - TODO: Added an item. 2000-08-24 00:57 arensb - Palm/PDB.pm: Clarified pod somewhat. 2000-08-24 00:29 arensb - util/pdbdump: (feature): Expands arrays of hash references recursively. 2000-08-13 18:08 arensb - Palm/Datebook.pm: Incorporated changes suggested by John Jannotti , to avoid warnings under Perl 5.6, when various fields are undefined. 2000-08-13 17:59 arensb - Palm/: Address.pm, Datebook.pm, Mail.pm, Memo.pm, ToDo.pm: Updated for new &parse_StdAppInfo behavior. 2000-08-13 17:59 arensb - Palm/StdAppInfo.pm: (bug fix): &parse_StdAppInfo now puts the non-category part of the AppInfo block in $appinfo->{other}. This is so that a script that reads a random .pdb file as a StdAppInfo (and nothing else) can copy it without loss of data. &pack_StdAppInfo uses $appinfo->{other}. Clarified documentation somewhat. 2000-08-04 00:12 arensb - Palm/PDB.pm: Fixed expression that made 'perl5.6 -w' print a warning. 2000-08-01 23:20 arensb - Makefile.PL: Bumped up version number. 2000-08-01 23:19 arensb - util/pdbdump: (bug fix): Read files in binary mode under Windows, MS-DOS, and other OSes that make that distinction. 2000-08-01 23:19 arensb - Palm/PDB.pm: (bug fix): Read and write files in binary mode under Windows, MS-DOS, and other OSes that make that distinction. 2000-08-01 23:17 arensb - Palm/Datebook.pm: Delete raw data field at the end of &ParseRecord. 2000-08-01 23:16 arensb - TODO: Added, removed some items. 2000-07-20 00:17 arensb - Makefile.PL: Bumped up version number. 2000-07-20 00:17 arensb - Palm/Datebook.pm: (Bug fix): Test for undefined record fields, to avoid warnings with databases built from scratch. Patch supplied by . 2000-07-19 00:00 arensb - Makefile.PL: Bumped up version number. 2000-07-19 00:00 arensb - TODO: Removed an item. 2000-07-18 23:58 arensb - Palm/: Address.pm, Datebook.pm, Mail.pm, Memo.pm, PDB.pm, ToDo.pm: Clarified the documentation for &new_Record. 2000-07-18 23:57 arensb - TODO: Added an item. 2000-07-08 20:25 arensb - Palm/PDB.pm: PDB::new now initializes more fields. PDB::Write prints a warning if writing a zero-length record. PDB::new_Resource: type now defaults to all NULs (instead of four spaces). 2000-07-08 20:11 arensb - Palm/Datebook.pm: Changed a comment. 2000-06-30 11:41 arensb - Makefile.PL: Bumped up version number. 2000-06-30 11:41 arensb - TODO: Added an item. 2000-06-30 11:41 arensb - Palm/Datebook.pm: Made AppInfo block initialization the same as other modules, even though Datebook doesn't really use categories and such. 2000-06-30 11:40 arensb - Palm/StdAppInfo.pm: (bug fix): Use of anonymous array instead of a real array caused first category (and first category ID) to be a reference rather than a "real" scalar. 2000-05-23 02:56 arensb - Makefile.PL: Incremented patchlevel number. 2000-05-23 02:55 arensb - Palm/: Address.pm, Memo.pm: (bug fix): Don't run StdAppInfo::import(). This prevents StdAppInfo from registering itself as the default handler in generic tools. 2000-05-13 16:30 arensb - Makefile.PL: Bumped up version number. 2000-05-13 16:29 arensb - README: Added contributor. 2000-05-13 16:29 arensb - Palm/Mail.pm: Fixed inconsistency in record member names ("replyTo" was sometimes "reply_to", and "sentTo" was sometimes "sent_to"). Patch supplied by Sumant S.R. Oemrawsingh 2000-05-13 01:16 arensb - Makefile.PL: Incremented patch level. Added "pdbdump" to the distribution. 2000-05-13 01:15 arensb - MANIFEST: Ship "pdbdump" instead of "dumpdb". 2000-05-13 01:15 arensb - util/dumpdb: Some obsolete refinements. 2000-05-13 01:14 arensb - Palm/StdAppInfo.pm: Added an import() function, so you can say that a function is a Palm::StdAppInfo. 2000-05-13 01:12 arensb - Palm/Mail.pm: (bug fix): Palm::Mail::new_Record() didn't return a value. Don't import StdAppInfo, since don't want new PDBs to default to StdAppInfo. 2000-05-08 05:48 arensb - util/pdbdump: Rewritten from scratch. This is basically just Palm/PDB.pm with trace statements. 2000-05-08 05:48 arensb - util/pdbdump: intermediate 2000-05-07 02:33 arensb - Palm/: StdAppInfo.pm, ToDo.pm: Removed unnecessary quoting in class members. 2000-05-07 02:33 arensb - Palm/Raw.pm: Removed unnecessary quoting in class members. Added `use strict'. Rearranged some variables at the top. 2000-05-07 02:30 arensb - Palm/: Datebook.pm, Mail.pm, Memo.pm, PDB.pm: Removed unnecessary quoting in class members. 2000-05-06 17:48 arensb - Palm/Address.pm: Removed unnecessary quoting in class members. 2000-05-06 17:20 arensb - Palm/ToDo.pm: Converted to use StdAppInfo. Fixed man page section in "see also" section. 2000-05-06 17:19 arensb - Palm/StdAppInfo.pm: Added a "to do" comment. 2000-05-06 17:18 arensb - Palm/: PDB.pm, Raw.pm: Fixed man page sections for "see also" section. 2000-04-29 17:53 arensb - Makefile.PL: Bumped up patch level. 2000-04-29 17:49 arensb - README: Added Robert Norris's patch for empty PDBs. 2000-04-29 16:52 arensb - Palm/PDB.pm: Applied Robert Norris 's patch: _load_appinfo_block used to die if the database contained no records. 2000-04-24 06:02 arensb - util/pdbdump: (added) This will eventually become the successor to dumpdb. 2000-04-24 06:01 arensb - util/dumpdb: More AppInfo block processing. 2000-04-24 06:01 arensb - Palm/StdAppInfo.pm: (added) Class for dealing with the standard part of AppInfo blocks (category support). 2000-04-24 06:00 arensb - Palm/PDB.pm: Uses `use strict'. Changed the way $VERSION is generated from RCS tag. Fixed the POD a bit. 2000-04-24 05:59 arensb - Palm/: Mail.pm, Memo.pm: Use StdAppInfo. 2000-04-24 05:56 arensb - Palm/Address.pm: Fixed manual section numbers. 2000-04-24 05:56 arensb - Palm/: Address.pm, Datebook.pm: Use StdAppInfo. 2000-04-24 05:55 arensb - TODO: Removed some stuff. 2000-04-24 05:55 arensb - Makefile.PL: Incremented version number. 2000-04-24 05:54 arensb - MANIFEST: Added "TODO", "StdAppInfo.pm" 2000-04-20 01:42 arensb - Palm/Mail.pm: Forgot to mention the subject in the documentation. 2000-02-01 23:45 arensb - TODO: (added): Things that need to be done. 2000-02-01 23:45 arensb - README: (added): README file. 2000-02-01 23:40 arensb - Makefile.PL: Incremented version number. 2000-02-01 23:21 arensb - Palm/Raw.pm: Rearranged the POD. Inherit &new, &new_Record methods. 2000-02-01 23:19 arensb - Palm/: Datebook.pm, Mail.pm, Memo.pm, ToDo.pm: Rearranged the POD. Added &new, &new_Record methods. 2000-02-01 23:18 arensb - Palm/Address.pm: Added placeholder for $pdb->{appinfo}{lastUniqueID}. 2000-02-01 07:31 arensb - Palm/: Raw.pm, ToDo.pm: Added $VERSION variable, per CPAN. 2000-02-01 07:31 arensb - Palm/PDB.pm: Renamed &deleteRecord to &delete_Record, for consistency. &append_Record, &append_Resource, &delete_Record update PDB's mtime. 2000-02-01 07:20 arensb - Palm/: Mail.pm, Memo.pm: Added $VERSION variable, per CPAN. 2000-02-01 07:19 arensb - Palm/Address.pm: Added $VERSION to all .pm files, per CPAN. Moved pod documentation to be adjacent to the code, instead of at the end. Fixed &new so there are always 16 categories, one of which is named "Unfiled" with code 0. 2000-01-23 04:18 arensb - Palm/PDB.pm: The various _load_*() functions are no longer methods, since they shouldn't be overridden anyway. Added documentation for new_Record(), append_Record(), new_Resource(), append_resource(), findRecordByID(), deleteRecord(). 2000-01-23 01:33 arensb - util/dumpdb: Bug fix: deal with PDBs without records, and PRCs without resources. Memo: Dump() now prints the AppInfo and sort blocks, if they exist. Address: Dump() now prints the field labels in the AppInfo block. Address: print the record attributes. 2000-01-23 01:28 arensb - Palm/PDB.pm: Updated copyright. Fixed function cross-references in the documentation. new() method now initializes the common fields. Fiddled with quotes in the documentation some more. Write() method now supplies the two NULs if they're missing. Added methods: new_Record(), append_Record(), new_Resource(), append_Resource(), findRecordByID(), deleteRecord(). 2000-01-23 01:24 arensb - Palm/Address.pm: Added new() method, to specifically create a new Address PDB. Added new_Record() method, to create a new Address record. Bug fix: PackAppInfoBlock() only packed whichever categories were defined. It now makes sure that it packs 16 categories. 2000-01-23 01:20 arensb - Makefile.PL: Incremented version. 1999-12-05 23:11 arensb - Palm/Address.pm: Fixed documentation to reflect reality. 1999-11-18 13:20 arensb - Palm/PDB.pm: Added magic $VERSION, to make CPAN happy. 1999-11-18 03:29 arensb - util/dumpdb: Took out Bitmaps and DopeWars, since those aren't part of the distribution (yet?). 1999-11-18 03:28 arensb - MANIFEST: Added README. 1999-11-18 03:24 arensb - Palm/Raw.pm: Clarified the use of "use Palm::Raw;" vs. "use Palm::Raw();" in the documentation. 1999-11-18 01:20 arensb - Palm/: Address.pm, Datebook.pm, Mail.pm, Memo.pm, PDB.pm, Raw.pm, ToDo.pm: Changed Palm::PDB:: to Palm:: where required. 1999-11-18 01:19 arensb - Makefile.PL: Changed distribution name to p5-Palm, for clarity. Explicitly set installation method. 1999-11-18 00:51 arensb - Makefile.PL: Fixed NAME and VERSION. 1999-11-18 00:51 arensb - MANIFEST: Removed Palm/Makefile.PL 1999-11-18 00:48 arensb - Palm/Makefile.PL: Deleted. It's not necessary. 1999-11-18 00:26 arensb - MANIFEST: Fixed after directory shuffle. 1999-11-18 00:24 arensb - test.pl: Generated by MakeMaker. Not used. 1999-11-18 00:23 arensb - util/dumpdb: Added RCS comment. Updated "use" statements to reflect the fact that the Palm stuff is in Palm::, not Palm::PDB::. 1999-11-18 00:21 arensb - util/copydb: Added RCS header. Took out the "use lib" line, for now. Changed "use" statements: the Palm stuff is no longer Palm::PDB:, but rather Palm::. 1999-11-18 00:17 arensb - util/dumpdb: Test utility to dump the contents of a PDB. 1999-11-18 00:17 arensb - util/copydb: Test utility to copy a PDB. 1999-11-18 00:16 arensb - Palm/ToDo.pm: Module for dealing with ToDo databases. 1999-11-18 00:16 arensb - Palm/Raw.pm: Module for dealing with "Raw" databases. 1999-11-18 00:16 arensb - Palm/Memo.pm: Module for dealing with Memo databases. 1999-11-18 00:16 arensb - Palm/Mail.pm: Module for dealing with Mail databases. 1999-11-18 00:16 arensb - Palm/Datebook.pm: Module for dealing with DateBook databases. 1999-11-18 00:15 arensb - Palm/Address.pm: Module for dealing with AddressBook databases. 1999-11-18 00:15 arensb - Palm/PDB.pm: Base module for dealing with PDBs. 1999-11-18 00:14 arensb - Palm/Makefile.PL: MakeMaker-generated Makefile.PL. 1999-11-18 00:10 arensb - Makefile.PL: MakeMaker-generated Makefile.PL. 1999-11-18 00:10 arensb - MANIFEST: First draft of manifest. Palm-PDB-1.400/README0000644000175000017500000000420512476641116012007 0ustar cjmcjmPalm-PDB version 1.400, released March 7, 2015 This distribution contains Palm::PDB and Palm::Raw, a pair of Perl 5 modules for reading, manipulating, and writing the .pdb and .prc database files used by PalmOS devices such as the PalmPilot and its successors. These 2 modules were historically part of the p5-Palm distribution, which is now just called Palm. They have been split off into their own distribution because they are also useful for manipulating the Mobipocket e-book format, which is also used for Amazon Kindle books. INSTALLATION To install this module, run the following commands: perl Makefile.PL make make test make install CHANGES Here's what's new in version 1.400 of Palm-PDB: (See the file "Changes" for the full revision history.) - Bump version number to be greater than all previous releases. Palm::PDB 1.36 was released in p5-Palm 1.008, but p5-Palm 1.009 contained Palm::PDB 1.009. Version numbers shouldn't go backwards. (reported by David Cantrell - GitHub#1) CONTRIBUTING The source is in GitHub: https://github.com/madsen/Palm-PDB If you have a change, fork the repo, make your patch, and send me a pull request. Don't be scared; it's easy! AUTHORS AND CONTRIBUTORS Andrew Arensburger : principal perpetrator Robert Norris : patch for empty PDBs Sumant S.R. Oemrawsingh : patch for inconsistent record fields in Mail.pm John Jannotti : patches to make Perl 5.6 shut up. John-David Smith : added "archive" attribute. Kenneth Lorber : fixed "pdbdump" to handle arrays in parsed records (e.g., Datebook exceptions). Alessandro Zummo : DateTime.pm, ZirePhoto.pm Kurt Starsinic : documentation patch. Seb Wills : documentation. brian d foy Christopher J. Madsen COPYRIGHT AND LICENSE This software is copyright (c) 2000 by Andrew Arensburger. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.