Palm::PDB(3pm)

๐Ÿ“ฆ
version 1.400 ยท 5 versions
, in debian trixie, ubuntu resolute, ubuntu stonking

๐Ÿ’– NAME ๐Ÿ’–

okay bestie, this is Palm::PDB(3pm), and the module itself is Palm::PDB โ€” it parses Palm database files. periodt. ๐Ÿ’…

๐ŸŒธ VERSION ๐ŸŒธ

This document describes version 1.400 of Palm::PDB, released March 7, 2015 as part of Palm-PDB version 1.400. ๐ŸŽ€

โœจ 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 Palm::PDB, even if you're dealing with some other type of database. $pdb will be reblessed to the appropriate type by $pdb->Load.) ๐Ÿซถ

๐Ÿฆ‹ 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 (.pdb) and Palm Resource (.prc) files. iconic behavior, honestly ๐Ÿ’–

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. it's giving team effort energy, and we love that for it. โœจ

The Palm::PDB module encapsulates the common work of parsing the structure of a Palm database. The Load() function reads the file, then passes the individual chunks (header, records, etc.) to application-specific functions for processing. Similarly, the Write() function calls application-specific functions to get the individual chunks, then writes them to a file. slay ๐Ÿ”ฅ

โœจ METHODS โœจ

๐Ÿ’– new

$new = Palm::PDB->new;

Makes you a brand-new PDB, no cap. $new is a reference to an anonymous hash, bestie โ€” and lowkey some of those hash elements are special, they have Actual Meaning. Go peek at Load() for the tea. ๐Ÿซ–


๐Ÿ’– RegisterPDBHandlers

&Palm::PDB::RegisterPDBHandlers("classname", typespec...);

Usually it looks like this, okay:

&Palm::PDB::RegisterPDBHandlers(__PACKAGE__,
      [ "FooB", "DATA" ],
      );

So here's the vibe: the $pdb->Load() method is basically a virtual constructor, a little shapeshifter โœจ. When it reads the header of a .pdb file, it goes looking up that file's creator and type in a bunch of tables, and then it reblesses $pdb into a class that actually knows how to parse the app-specific bits (AppInfo block, records, etc.).

RegisterPDBHandlers() is what adds entries to those tables. It's saying: any file whose creator and/or type matches any of the typespecs (and there can be several, we love options ๐Ÿ’…) should get reblessed into the class classname.

Heads up bestie: RegisterPDBHandlers() only applies to record databases (.pdb files). For resource databases you want RegisterPRCHandlers() โ€” don't mix them up, it's giving "wrong door at the party." ๐Ÿšช

The usual move is calling RegisterPDBHandlers() inside the import() function of a helper class. In that case the class is registering itself, so the cutest thing is just to use "__PACKAGE__" for the package name:

package PalmFoo;
use Palm::PDB;

sub import
{
    &Palm::PDB::RegisterPDBHandlers(__PACKAGE__,
        [ "FooZ", "DATA" ]
        );
}

Now, a typespec can be either a plain string, or an anonymous array with two elements. If it's an anonymous array? Then element one is the file's creator, and element two is its type. If it's a string? That's equivalent to saying "this string is the database's creator, and the type is a wildcard." Slay. ๐ŸŒˆ

The creator and type should each be either four-character strings, or the empty string. And an empty string means wildcard. So check this out:

&Palm::PDB::RegisterPDBHandlers("MyClass",
    [ "fOOf", "DATA" ],
    [ "BarB", "" ],
    [ "", "BazQ" ],
    "Fred"
    );

Class MyClass will handle, in order:

  • Databases whose creator is "fOOf" and whose type is "DATA".
  • Databases whose creator is "BarB", of any type.
  • Databases with any creator whose type is "BazQ".
  • Databases whose creator is "Fred", of any type.

๐Ÿ’– RegisterPRCHandlers

&Palm::PDB::RegisterPRCHandlers("classname", typespec...);

Typically, like:

&Palm::PDB::RegisterPRCHandlers(__PACKAGE__,
      [ "FooZ", "CODE" ],
      );

RegisterPRCHandlers() is the sister function to RegisterPDBHandlers(), except it names a class to handle resource database (.prc) files instead. Two siblings, two jobs, iconic honestly. ๐Ÿ‘‘

A class that parses applications should start like this:

package PalmApps;
use Palm::PDB;

sub import
{
    &Palm::PDB::RegisterPRCHandlers(__PACKAGE__,
        [ "", "appl" ]
        );
}

๐Ÿ’– Load

$pdb->Load($filename);

Reads the file $filename, parses it, reblesses $pdb into the right class, and then calls the appropriate methods to parse the database's app-specific parts (go see "HELPER CLASS METHODS" for that lore).

Plot twist: $filename can also be an open file handle ๐Ÿซถ โ€” as long as it's seekable. That's what lets you work with databases sitting in memory structures.

Load() decides how to rebless $pdb using the typespecs you fed to RegisterPDBHandlers() and RegisterPRCHandlers(). For record databases it uses the ones passed to RegisterPDBHandlers(), and for resource databases it uses the ones passed to RegisterPRCHandlers().

And here's the priority order โ€” Load() hunts for matching typespecs from most specific to least specific:

  1. A typespec that specifies both the database's creator and its type exactly.
  2. A typespec that specifies the database's type and has a wildcard for the creator (this one is rarely used, ngl).
  3. A typespec that specifies the database's creator and has a wildcard for the type.
  4. A typespec that has wildcards for both the creator and type.

So if a database has creator "FooZ" and type "DATA", Load() looks for "FooZ"/"DATA" first, then ""/"DATA", then "FooZ"/"", and finally falls back on ""/"" โ€” the universal default, the "everybody's welcome" option. ๐Ÿ’–

Once Load() comes back, $pdb might be holding these fields:

  • $pdb->{"name"} โ€” the name of the database.
  • $pdb->{"attributes"}{"ResDB"}
  • $pdb->{"attributes"}{"ReadOnly"}
  • $pdb->{"attributes"}{"AppInfoDirty"}
  • $pdb->{"attributes"}{"Backup"}
  • $pdb->{"attributes"}{"OKToInstallNewer"}
  • $pdb->{"attributes"}{"ResetAfterInstall"}
  • $pdb->{"attributes"}{"CopyPrevention"}
  • $pdb->{"attributes"}{"Stream"}
  • $pdb->{"attributes"}{"Hidden"}
  • $pdb->{"attributes"}{"LaunchableData"}
  • $pdb->{"attributes"}{"Recyclable"}
  • $pdb->{"attributes"}{"Bundle"}
  • $pdb->{"attributes"}{"Open"} โ€” these are the attribute flags straight out of the database header. Each one is true iff its corresponding flag is set.

The "LaunchableData" attribute is set on PQAs.

  • $pdb->{"version"} โ€” the database's version number. An integer.
  • $pdb->{"ctime"}
  • $pdb->{"mtime"}
  • $pdb->{"baktime"} โ€” the database's creation time, last modification time, and time of last backup, all in Unix "time_t" format (that's seconds since Jan. 1, 1970, bestie ๐Ÿ•ฐ๏ธ).
  • $pdb->{"modnum"} โ€” the database's modification number. An integer.
  • $pdb->{"type"} โ€” the database's type. A four-character string.
  • $pdb->{"creator"} โ€” the database's creator. A four-character string.
  • $pdb->{"uniqueIDseed"} โ€” the database's unique ID seed. An integer.
  • $pdb->{"2NULs"} โ€” the two NUL bytes that show up after the record index and the AppInfo block. It's in here because every once in a loooong while, they just... aren't NULs, for some reason. ๐Ÿ˜ญ
  • $pdb->{"appinfo"} โ€” the AppInfo block, exactly as returned by the $pdb->ParseAppInfoBlock() helper method.
  • $pdb->{"sort"} โ€” the sort block, exactly as returned by the $pdb->ParseSortBlock() helper method.
  • @{$pdb->{"records"}} โ€” the list of records in the database, as returned by the $pdb->ParseRecord() helper method. Resource databases do not have this one.
  • @{$pdb->{"resources"}} โ€” the list of resources in the database, as returned by the $pdb->ParseResource() helper method. Record databases do not have this one.

All of these fields are fair game to set by hand, but they should conform to the format given above. No freestyling that breaks the schema, please. ๐Ÿ’…


๐Ÿ’– Write

$pdb->Write($filename);

Calls methods in the helper classes to grab the app-specific parts of the database, then writes the whole database out to the file $filename.

Same deal as Load(): $filename can also be an open file handle (as long as it's seekable ๐Ÿซถ), which is what lets you manipulate databases living in memory structures.

Write() uses these helper methods:

  • PackAppInfoBlock()
  • PackSortBlock()
  • PackResource() or PackRecord()

Also go read "HELPER CLASS METHODS". โœจ


๐Ÿ’– new_Record

$record = Palm::PDB->new_Record();

Makes a new record with the absolute bare minimum, the "just woke up, no makeup" version:

$record->{'category'}
$record->{'attributes'}{'Dirty'}
$record->{'id'}

The ``Dirty'' attribute starts out set, because this function usually gets called to create records that are about to be added to a database. Makes sense, right?

Important: "new_Record" does not add the new record to a PDB. If that's what you want, you're looking for "append_Record". Don't @ me.

๐Ÿ’– 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. That covers changes made via function calls (any call that updates the $pdb's "last modification" time) and checking the "dirty" status of attributes where it's actually possible to check โ€” that means AppInfo, records, but not resource entries. ๐Ÿ™ˆ


๐Ÿ’– append_Record

$record  = $pdb->append_Record;
$record2 = $pdb->append_Record($record1);

Called with no arguments? It makes a new record with new_Record(), and appends it to $pdb.

Given a reference to a record? It appends that record to @{$pdb->{records}}.

Either way you get back a reference to the newly-appended record. ๐ŸŽ€

This method updates $pdb's "last modification" time.


๐Ÿ’– new_Resource

$resource = Palm::PDB->new_Resource();

Makes a new resource and initializes these:

$resource->{type}
$resource->{id}

๐Ÿ’– append_Resource

$resource  = $pdb->append_Resource;
$resource2 = $pdb->append_Resource($resource1);

No arguments? It creates a new resource with new_Resource(), and appends it to $pdb.

Given a reference to a resource? It appends that resource to @{$pdb->{resources}}.

You get back a reference to the newly-appended resource. ๐Ÿฆ‹

This method updates $pdb's "last modification" time.


๐Ÿ’– findRecordByID

$record = $pdb->findRecordByID($id);

Digs 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. Sometimes the answer is just "she's not here." ๐Ÿ’€


๐Ÿ’– delete_Record

$pdb->delete_Record($record, $expunge);

Marks $record for deletion, so it'll get deleted from the database at the next sync. It's a flag, not a goodbye yet. ๐Ÿ‘‹

If $expunge is false or just omitted, the record gets marked for deletion with archival. If $expunge is true, it's marked for deletion without archival.

This method updates $pdb's "last modification" time.


๐Ÿ’– remove_Record

for (@{ $pdb->{'records'} })
{
        $pdb->remove_Record( $_ ) if $_->{attributes}{deleted};
}

Removes $record from the database. This is different from "delete_Record" because it's an actual deletion, not just setting a flag. Like, she's gone-gone. โœจ

This method updates $pdb's "last modification" time.

๐Ÿ’… HELPER CLASS METHODS ๐Ÿ’…

okay so plot twist bestie: $pdb->Load() reblesses $pdb into a whole new class. โœจ Not a glow-up, a reblessing โ€” like the object gets a new identity and a new job. This new class is the helper class, and its whole thing is converting raw data from the database into parsed representations of it, and vice-versa. ๐Ÿ”

And a helper class must have all of the methods listed below. All of them, no cap. ๐Ÿ‘‘

But real talk: if you're sitting there like "girl do I really have to define every single one of these" โ€” no babe, you do not. Palm::Raw is useful if you don't want to define all of the required methods. It's the shortcut, it's the cheat code, it's giving convenience. ๐Ÿซถ


๐ŸŒธ ParseAppInfoBlock ๐ŸŒธ

$appinfo = $pdb->ParseAppInfoBlock($buf);

$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. So if there's no AppInfo block, this literally never happens, and that's fine.

Whatever ParseAppInfoBlock() hands back will be accessible as "$pdb->{appinfo}". ๐Ÿ“Œ

๐Ÿ’ซ PackAppInfoBlock ๐Ÿ’ซ

$buf = $pdb->PackAppInfoBlock();

This is the converse of ParseAppInfoBlock() โ€” the reverse uno card. ๐Ÿƒ It takes $pdb's AppInfo block, "$pdb->{appinfo}", and returns a string of binary data that can be written to the database file. โœ๏ธ

๐Ÿฆ‹ ParseSortBlock ๐Ÿฆ‹

$sort = $pdb->ParseSortBlock($buf);

$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 "$pdb->{sort}". ๐Ÿ“Œ

๐ŸŒˆ PackSortBlock ๐ŸŒˆ

$buf = $pdb->PackSortBlock();

This is the converse of ParseSortBlock(). It takes $pdb's sort block, "$pdb->{sort}", and returns a string of raw data that can be written to the database file. โœ๏ธ

๐Ÿ”ฅ 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 "@{$pdb->{records}}". And the records appear in this list in the same order as they appear in the file. Order is preserved, periodt. ๐Ÿ”ฅ

The $offset argument is not normally useful, but is included for completeness. It's the decorative pillow of arguments. ๐Ÿ›‹๏ธ

The fields in %$attributes are boolean values. They are true iff the record has the corresponding flag set.

$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;
}

(Yes, that's exactly how it is. Copy it character for character, bestie. ๐Ÿ’…)

โœจ 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. ๐Ÿšซ Never ever. Not once.

๐Ÿงš 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 "@{$pdb->{resources}}". 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. Again with the decorative pillow. ๐Ÿ›‹๏ธ

๐Ÿ‘‘ 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. (Yep โ€” that's written exactly as-is up there. We don't fix the docs, we just vibe with them. ๐Ÿ’€)

PackResource() is never called when writing a record database. ๐Ÿšซ


๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

  • Palm::Raw
  • Palm::Address
  • Palm::Datebook
  • Palm::Mail
  • Palm::Memo
  • Palm::ToDo
  • Palm Database Files, in the ColdSync distribution.
  • The Virtual Constructor (aka Factory Method) pattern is described in Design Patterns, by Erich Gamma et al., Addison-Wesley.

โš™๏ธ CONFIGURATION AND ENVIRONMENT โš™๏ธ

Palm::PDB requires no configuration files or environment variables. Zero. None. Nothing to set up, nothing to export, nothing to forget. It's giving low-maintenance. ๐Ÿง˜โ€โ™€๏ธ


๐Ÿšซ INCOMPATIBILITIES ๐Ÿšซ

None reported. โœจ That's the whole section, bestie. Nothing to see here.


๐Ÿ’€ BUGS AND LIMITATIONS ๐Ÿ’€

  • These functions die too easily. ๐Ÿ˜ญ They should return an error code like a mature little library, but nope. Straight to dying.
  • Database manipulation is still an arcane art. ๐Ÿ”ฎ Truly. Ancient magic.
  • It may be possible to parse sort blocks further. So like, there's more on the table there, potentially.

๐Ÿซถ AUTHORS ๐Ÿซถ

  • Andrew Arensburger <arensb AT ooblick.com>
  • Currently maintained by Christopher J. Madsen <perl AT cjmweb.net>

Please report any bugs or feature requests to <bug-Palm-PDB AT rt.cpan.org>, or through the web interface at <http://rt.cpan.org/Public/Bug/Report.html?Queue=Palm-PDB>. ๐Ÿ“ฎ

You can follow or contribute to Palm-PDB's development at <https://github.com/madsen/Palm-PDB>. ๐Ÿ‘ฉโ€๐Ÿ’ป


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. ๐Ÿช Free as in free, bestie.


โš ๏ธ DISCLAIMER OF WARRANTY โš ๏ธ

okay so this part is the legal one and it is ALL IN CAPS because that's how lawyers do, and we are preserving every single word of it, no edits, no vibes-based paraphrasing. Read it. It matters. ๐Ÿ‘‡

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.

and that's the whole page babes ๐ŸŽ€ this one came out of perl v5.38.2 and it's dated 2024-03-07 โ€” so if you're reading this on some future date, hi, hope Palm Pilots are cool again ๐Ÿ’– go parse something iconic, you've got this ๐Ÿ‘‘โœจ

๐Ÿ“ฆ where this page comes from