| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | # PDB.pm | 
| 2 |  |  |  |  |  |  | # | 
| 3 |  |  |  |  |  |  | # Perl module for reading and writing Palm databases (both PDB and PRC). | 
| 4 |  |  |  |  |  |  | # | 
| 5 |  |  |  |  |  |  | #	Copyright (C) 1999, 2000, Andrew Arensburger. | 
| 6 |  |  |  |  |  |  | #	You may distribute this file under the terms of the Artistic | 
| 7 |  |  |  |  |  |  | #	License, as specified in the README file. | 
| 8 |  |  |  |  |  |  | # | 
| 9 |  |  |  |  |  |  | # $Id: PDB.pm,v 1.29 2002/11/03 16:43:16 azummo Exp $ | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | # A Palm database file (either .pdb or .prc) has the following overall | 
| 12 |  |  |  |  |  |  | # structure: | 
| 13 |  |  |  |  |  |  | #	Header | 
| 14 |  |  |  |  |  |  | #	Index header | 
| 15 |  |  |  |  |  |  | #	Record/resource index | 
| 16 |  |  |  |  |  |  | #	Two NUL(?) bytes | 
| 17 |  |  |  |  |  |  | #	Optional AppInfo block | 
| 18 |  |  |  |  |  |  | #	Optional sort block | 
| 19 |  |  |  |  |  |  | #	Records/resources | 
| 20 |  |  |  |  |  |  | # See http://www.palmos.com/dev/tech/docs/fileformats.zip | 
| 21 |  |  |  |  |  |  | # for details. | 
| 22 |  |  |  |  |  |  |  | 
| 23 | 9 |  |  | 9 |  | 42 | use strict; | 
|  | 9 |  |  |  |  | 15 |  | 
|  | 9 |  |  |  |  | 374 |  | 
| 24 |  |  |  |  |  |  | package EBook::MOBI::MobiPerl::Palm::PDB; | 
| 25 | 9 |  |  | 9 |  | 46 | use vars qw( $VERSION %PDBHandlers %PRCHandlers ); | 
|  | 9 |  |  |  |  | 18 |  | 
|  | 9 |  |  |  |  | 40311 |  | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | # One liner, to allow MakeMaker to work. | 
| 28 |  |  |  |  |  |  | $VERSION = do { my @r = (q$Revision: 1.29 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | =head1 NAME | 
| 31 |  |  |  |  |  |  |  | 
| 32 |  |  |  |  |  |  | Palm::PDB - Parse Palm database files. | 
| 33 |  |  |  |  |  |  |  | 
| 34 |  |  |  |  |  |  | =head1 SYNOPSIS | 
| 35 |  |  |  |  |  |  |  | 
| 36 |  |  |  |  |  |  | use Palm::PDB; | 
| 37 |  |  |  |  |  |  | use SomeHelperClass; | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | $pdb = new Palm::PDB; | 
| 40 |  |  |  |  |  |  | $pdb->Load("myfile.pdb"); | 
| 41 |  |  |  |  |  |  |  | 
| 42 |  |  |  |  |  |  | # Manipulate records in $pdb | 
| 43 |  |  |  |  |  |  |  | 
| 44 |  |  |  |  |  |  | $pdb->Write("myotherfile.pdb"); | 
| 45 |  |  |  |  |  |  |  | 
| 46 |  |  |  |  |  |  | (Note: yes, you do want to use C, even if you're dealing | 
| 47 |  |  |  |  |  |  | with some other type of database. $pdb will be reblessed to the | 
| 48 |  |  |  |  |  |  | appropriate type by C<$pdb-ELoad>.) | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | The Palm::PDB module provides a framework for reading and writing | 
| 53 |  |  |  |  |  |  | database files for use on PalmOS devices such as the PalmPilot. It can | 
| 54 |  |  |  |  |  |  | read and write both Palm Database (C<.pdb>) and Palm Resource | 
| 55 |  |  |  |  |  |  | (C<.prc>) files. | 
| 56 |  |  |  |  |  |  |  | 
| 57 |  |  |  |  |  |  | By itself, the PDB module is not terribly useful; it is intended to be | 
| 58 |  |  |  |  |  |  | used in conjunction with supplemental modules for specific types of | 
| 59 |  |  |  |  |  |  | databases, such as Palm::Raw or Palm::Memo. | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | The Palm::PDB module encapsulates the common work of parsing the | 
| 62 |  |  |  |  |  |  | structure of a Palm database. The L function reads the file, | 
| 63 |  |  |  |  |  |  | then passes the individual chunks (header, records, etc.) to | 
| 64 |  |  |  |  |  |  | application-specific functions for processing. Similarly, the | 
| 65 |  |  |  |  |  |  | L function calls application-specific functions to get the | 
| 66 |  |  |  |  |  |  | individual chunks, then writes them to a file. | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | =head1 METHODS | 
| 69 |  |  |  |  |  |  |  | 
| 70 |  |  |  |  |  |  | =cut | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | my $EPOCH_1904 = 2082844800;		# Difference between Palm's | 
| 73 |  |  |  |  |  |  | # epoch (Jan. 1, 1904) and | 
| 74 |  |  |  |  |  |  | # Unix's epoch (Jan. 1, 1970), | 
| 75 |  |  |  |  |  |  | # in seconds. | 
| 76 |  |  |  |  |  |  | my $HeaderLen = 32+2+2+(9*4);		# Size of database header | 
| 77 |  |  |  |  |  |  | my $RecIndexHeaderLen = 6;		# Size of record index header | 
| 78 |  |  |  |  |  |  | my $IndexRecLen = 8;			# Length of record index entry | 
| 79 |  |  |  |  |  |  | my $IndexRsrcLen = 10;			# Length of resource index entry | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  | %PDBHandlers = ();			# Record handler map | 
| 82 |  |  |  |  |  |  | %PRCHandlers = ();			# Resource handler map | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | =head2 new | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | $new = new Palm::PDB(); | 
| 87 |  |  |  |  |  |  |  | 
| 88 |  |  |  |  |  |  | Creates a new PDB. $new is a reference to an anonymous hash. Some of | 
| 89 |  |  |  |  |  |  | its elements have special significance. See L. | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | =cut | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  | sub new | 
| 94 |  |  |  |  |  |  | { | 
| 95 | 3 |  |  | 3 | 1 | 5 | my $class	= shift; | 
| 96 | 3 |  |  |  |  | 5 | my $params	= shift; | 
| 97 |  |  |  |  |  |  |  | 
| 98 | 3 |  |  |  |  | 6 | my $self = {}; | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  |  | 
| 101 |  |  |  |  |  |  | # Initialize the PDB. These values are just defaults, of course. | 
| 102 | 3 |  | 50 |  |  | 20 | $self->{'name'} 	= $params->{'name'}		|| ""; | 
| 103 | 3 |  | 50 |  |  | 18 | $self->{'attributes'}	= $params->{'attributes'} 	|| {}; | 
| 104 | 3 |  | 50 |  |  | 23 | $self->{'version'}	= $params->{'version'} 		|| 0; | 
| 105 |  |  |  |  |  |  |  | 
| 106 | 3 |  |  |  |  | 11 | my $now = time; | 
| 107 |  |  |  |  |  |  |  | 
| 108 | 3 |  | 33 |  |  | 15 | $self->{'ctime'} 	= $params->{'ctime'}		|| $now; | 
| 109 | 3 |  | 33 |  |  | 31 | $self->{'mtime'} 	= $params->{'mtime'}		|| $now; | 
| 110 | 3 |  | 33 |  |  | 15 | $self->{'baktime'} 	= $params->{'baktime'}		|| -$EPOCH_1904; | 
| 111 |  |  |  |  |  |  |  | 
| 112 | 3 |  | 50 |  |  | 19 | $self->{'modnum'}	= $params->{'modnum'}		|| 0; | 
| 113 | 3 |  | 50 |  |  | 15 | $self->{'type'}		= $params->{'type'}		|| "\0\0\0\0"; | 
| 114 | 3 |  | 50 |  |  | 15 | $self->{'creator'} 	= $params->{'creator'}		|| "\0\0\0\0"; | 
| 115 | 3 |  | 50 |  |  | 15 | $self->{'uniqueIDseed'} = $params->{'uniqueIDseed'}	|| 0; | 
| 116 |  |  |  |  |  |  |  | 
| 117 | 3 |  |  |  |  | 5 | $self->{"2NULs"}	= "\0\0"; | 
| 118 |  |  |  |  |  |  |  | 
| 119 | 3 |  |  |  |  | 6 | bless $self, $class; | 
| 120 | 3 |  |  |  |  | 9 | return $self; | 
| 121 |  |  |  |  |  |  | } | 
| 122 |  |  |  |  |  |  |  | 
| 123 |  |  |  |  |  |  | =head2 RegisterPDBHandlers | 
| 124 |  |  |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | &Palm::PDB::RegisterPDBHandlers("classname", typespec...); | 
| 126 |  |  |  |  |  |  |  | 
| 127 |  |  |  |  |  |  | Typically: | 
| 128 |  |  |  |  |  |  |  | 
| 129 |  |  |  |  |  |  | &Palm::PDB::RegisterPDBHandlers(__PACKAGE__, | 
| 130 |  |  |  |  |  |  | [ "FooB", "DATA" ], | 
| 131 |  |  |  |  |  |  | ); | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  | The $pdb->L method acts as a virtual constructor. When | 
| 134 |  |  |  |  |  |  | it reads the header of a C<.pdb> file, it looks up the file's creator | 
| 135 |  |  |  |  |  |  | and type in a set of tables, and reblesses $pdb into a class capable | 
| 136 |  |  |  |  |  |  | of parsing the application-specific parts of the file (AppInfo block, | 
| 137 |  |  |  |  |  |  | records, etc.) | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | RegisterPDBHandlers() adds entries to these tables; it says that any | 
| 140 |  |  |  |  |  |  | file whose creator and/or type match any of the Is (there | 
| 141 |  |  |  |  |  |  | may be several) should be reblessed into the class I. | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | Note that RegisterPDBHandlers() applies only to record databases | 
| 144 |  |  |  |  |  |  | (C<.pdb> files). For resource databases, see | 
| 145 |  |  |  |  |  |  | L. | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | RegisterPDBHandlers() is typically called in the import() function of | 
| 148 |  |  |  |  |  |  | a helper class. In this case, the class is registering itself, and it | 
| 149 |  |  |  |  |  |  | is simplest just to use C<__PACKAGE__> for the package name: | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | package PalmFoo; | 
| 152 |  |  |  |  |  |  | use Palm::PDB; | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  | sub import | 
| 155 |  |  |  |  |  |  | { | 
| 156 |  |  |  |  |  |  | &Palm::PDB::RegisterPDBHandlers(__PACKAGE__, | 
| 157 |  |  |  |  |  |  | [ "FooZ", "DATA" ] | 
| 158 |  |  |  |  |  |  | ); | 
| 159 |  |  |  |  |  |  | } | 
| 160 |  |  |  |  |  |  |  | 
| 161 |  |  |  |  |  |  | A I can be either a string, or an anonymous array with two | 
| 162 |  |  |  |  |  |  | elements. If it is an anonymous array, then the first element is the | 
| 163 |  |  |  |  |  |  | file's creator; the second element is its type. If a I is a | 
| 164 |  |  |  |  |  |  | string, it is equivalent to specifying that string as the database's | 
| 165 |  |  |  |  |  |  | creator, and a wildcard as its type. | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | The creator and type should be either four-character strings, or the | 
| 168 |  |  |  |  |  |  | empty string. An empty string represents a wildcard. Thus: | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | &Palm::PDB::RegisterPDBHandlers("MyClass", | 
| 171 |  |  |  |  |  |  | [ "fOOf", "DATA" ], | 
| 172 |  |  |  |  |  |  | [ "BarB", "" ], | 
| 173 |  |  |  |  |  |  | [ "", "BazQ" ], | 
| 174 |  |  |  |  |  |  | "Fred" | 
| 175 |  |  |  |  |  |  | ); | 
| 176 |  |  |  |  |  |  |  | 
| 177 |  |  |  |  |  |  | Class MyClass will handle: | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | =over 4 | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | =item Z<> | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | Databases whose creator is C and whose type is C. | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =item Z<> | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | Databases whose creator is C, of any type. | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =item Z<> | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | Databases with any creator whose type is C. | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | =item Z<> | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | Databases whose creator is C, of any type. | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | =back | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | =for html | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | =cut | 
| 203 |  |  |  |  |  |  | #'	<-- For Emacs. | 
| 204 |  |  |  |  |  |  |  | 
| 205 |  |  |  |  |  |  | sub RegisterPDBHandlers | 
| 206 |  |  |  |  |  |  | { | 
| 207 | 18 |  |  | 18 | 1 | 32 | my $handler = shift;		# Name of class that'll handle | 
| 208 |  |  |  |  |  |  | # these databases | 
| 209 | 18 |  |  |  |  | 38 | my @types = @_; | 
| 210 | 18 |  |  |  |  | 24 | my $item; | 
| 211 |  |  |  |  |  |  |  | 
| 212 | 18 |  |  |  |  | 39 | foreach $item (@types) | 
| 213 |  |  |  |  |  |  | { | 
| 214 | 18 | 50 |  |  |  | 65 | if (ref($item) eq "ARRAY") | 
| 215 |  |  |  |  |  |  | { | 
| 216 | 18 |  |  |  |  | 100 | $PDBHandlers{$item->[0]}{$item->[1]} = $handler; | 
| 217 |  |  |  |  |  |  | } else { | 
| 218 | 0 |  |  |  |  | 0 | $PDBHandlers{$item}{""} = $handler; | 
| 219 |  |  |  |  |  |  | } | 
| 220 |  |  |  |  |  |  | } | 
| 221 |  |  |  |  |  |  | } | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | =head2 RegisterPRCHandlers | 
| 224 |  |  |  |  |  |  |  | 
| 225 |  |  |  |  |  |  | &Palm::PDB::RegisterPRCHandlers("classname", typespec...); | 
| 226 |  |  |  |  |  |  |  | 
| 227 |  |  |  |  |  |  | Typically: | 
| 228 |  |  |  |  |  |  |  | 
| 229 |  |  |  |  |  |  | &Palm::PDB::RegisterPRCHandlers(__PACKAGE__, | 
| 230 |  |  |  |  |  |  | [ "FooZ", "CODE" ], | 
| 231 |  |  |  |  |  |  | ); | 
| 232 |  |  |  |  |  |  |  | 
| 233 |  |  |  |  |  |  | RegisterPRCHandlers() is similar to | 
| 234 |  |  |  |  |  |  | L, but specifies a class | 
| 235 |  |  |  |  |  |  | to handle resource database (C<.prc>) files. | 
| 236 |  |  |  |  |  |  |  | 
| 237 |  |  |  |  |  |  | A class for parsing applications should begin with: | 
| 238 |  |  |  |  |  |  |  | 
| 239 |  |  |  |  |  |  | package PalmApps; | 
| 240 |  |  |  |  |  |  | use Palm::PDB; | 
| 241 |  |  |  |  |  |  |  | 
| 242 |  |  |  |  |  |  | sub import | 
| 243 |  |  |  |  |  |  | { | 
| 244 |  |  |  |  |  |  | &Palm::PDB::RegisterPRCHandlers(__PACKAGE__, | 
| 245 |  |  |  |  |  |  | [ "", "appl" ] | 
| 246 |  |  |  |  |  |  | ); | 
| 247 |  |  |  |  |  |  | } | 
| 248 |  |  |  |  |  |  |  | 
| 249 |  |  |  |  |  |  | =cut | 
| 250 |  |  |  |  |  |  |  | 
| 251 |  |  |  |  |  |  | sub RegisterPRCHandlers | 
| 252 |  |  |  |  |  |  | { | 
| 253 | 18 |  |  | 18 | 1 | 31 | my $handler = shift;		# Name of class that'll handle | 
| 254 |  |  |  |  |  |  | # these databases | 
| 255 | 18 |  |  |  |  | 31 | my @types = @_; | 
| 256 | 18 |  |  |  |  | 28 | my $item; | 
| 257 |  |  |  |  |  |  |  | 
| 258 | 18 |  |  |  |  | 28 | foreach $item (@types) | 
| 259 |  |  |  |  |  |  | { | 
| 260 | 18 | 50 |  |  |  | 48 | if (ref($item) eq "ARRAY") | 
| 261 |  |  |  |  |  |  | { | 
| 262 | 18 |  |  |  |  | 266 | $PRCHandlers{$item->[0]}{$item->[1]} = $handler; | 
| 263 |  |  |  |  |  |  | } else { | 
| 264 | 0 |  |  |  |  | 0 | $PRCHandlers{$item}{""} = $handler; | 
| 265 |  |  |  |  |  |  | } | 
| 266 |  |  |  |  |  |  | } | 
| 267 |  |  |  |  |  |  | } | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | =head2 Load | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | $pdb->Load("filename"); | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | Reads the file F, parses it, reblesses $pdb to the | 
| 274 |  |  |  |  |  |  | appropriate class, and invokes appropriate methods to parse the | 
| 275 |  |  |  |  |  |  | application-specific parts of the database (see L). | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | Load() uses the Is given to RegisterPDBHandlers() and | 
| 278 |  |  |  |  |  |  | RegisterPRCHandlers() when deciding how to rebless $pdb. For record | 
| 279 |  |  |  |  |  |  | databases, it uses the Is passed to RegisterPDBHandlers(), | 
| 280 |  |  |  |  |  |  | and for resource databases, it uses the Is passed to | 
| 281 |  |  |  |  |  |  | RegisterPRCHandlers(). | 
| 282 |  |  |  |  |  |  |  | 
| 283 |  |  |  |  |  |  | Load() looks for matching Is in the following order, from | 
| 284 |  |  |  |  |  |  | most to least specific: | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | =over 4 | 
| 287 |  |  |  |  |  |  |  | 
| 288 |  |  |  |  |  |  | =item 1 | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | A I that specifies both the database's creator and its type | 
| 291 |  |  |  |  |  |  | exactly. | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | =item 2 | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | A I that specifies the database's type and has a wildcard | 
| 296 |  |  |  |  |  |  | for the creator (this is rarely used). | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | =item 3 | 
| 299 |  |  |  |  |  |  |  | 
| 300 |  |  |  |  |  |  | A I that specifies the database's creator and has a wildcard | 
| 301 |  |  |  |  |  |  | for the type. | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | =item 4 | 
| 304 |  |  |  |  |  |  |  | 
| 305 |  |  |  |  |  |  | A I that has wildcards for both the creator and type. | 
| 306 |  |  |  |  |  |  |  | 
| 307 |  |  |  |  |  |  | =back | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | =for html | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | Thus, if the database has creator "FooZ" and type "DATA", Load() will | 
| 313 |  |  |  |  |  |  | first look for "FooZ"/"DATA", then ""/"DATA", then "FooZ"/"", and | 
| 314 |  |  |  |  |  |  | finally will fall back on ""/"" (the universal default). | 
| 315 |  |  |  |  |  |  |  | 
| 316 |  |  |  |  |  |  | After Load() returns, $pdb may contain the following fields: | 
| 317 |  |  |  |  |  |  |  | 
| 318 |  |  |  |  |  |  | =over | 
| 319 |  |  |  |  |  |  |  | 
| 320 |  |  |  |  |  |  | =item $pdb-E{Z<>"name"Z<>} | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | The name of the database. | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"ResDB"Z<>} | 
| 325 |  |  |  |  |  |  |  | 
| 326 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"ReadOnly"Z<>} | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"AppInfoDirty"Z<>} | 
| 329 |  |  |  |  |  |  |  | 
| 330 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Backup"Z<>} | 
| 331 |  |  |  |  |  |  |  | 
| 332 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"OKToInstallNewer"Z<>} | 
| 333 |  |  |  |  |  |  |  | 
| 334 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"ResetAfterInstall"Z<>} | 
| 335 |  |  |  |  |  |  |  | 
| 336 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"CopyPrevention"Z<>} | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Stream"Z<>} | 
| 339 |  |  |  |  |  |  |  | 
| 340 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Hidden"Z<>} | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"LaunchableData"Z<>} | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Recyclable"Z<>} | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Bundle"Z<>} | 
| 347 |  |  |  |  |  |  |  | 
| 348 |  |  |  |  |  |  | =item $pdb-E{Z<>"attributes"Z<>}{Z<>"Open"Z<>} | 
| 349 |  |  |  |  |  |  |  | 
| 350 |  |  |  |  |  |  | These are the attribute flags from the database header. Each is true | 
| 351 |  |  |  |  |  |  | iff the corresponding flag is set. | 
| 352 |  |  |  |  |  |  |  | 
| 353 |  |  |  |  |  |  | The "LaunchableData" attribute is set on PQAs. | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | =item $pdb-E{Z<>"version"Z<>} | 
| 356 |  |  |  |  |  |  |  | 
| 357 |  |  |  |  |  |  | The database's version number. An integer. | 
| 358 |  |  |  |  |  |  |  | 
| 359 |  |  |  |  |  |  | =item $pdb-E{Z<>"ctime"Z<>} | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | =item $pdb-E{Z<>"mtime"Z<>} | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  | =item $pdb-E{Z<>"baktime"Z<>} | 
| 364 |  |  |  |  |  |  |  | 
| 365 |  |  |  |  |  |  | The database's creation time, last modification time, and time of last | 
| 366 |  |  |  |  |  |  | backup, in Unix C format (seconds since Jan. 1, 1970). | 
| 367 |  |  |  |  |  |  |  | 
| 368 |  |  |  |  |  |  | =item $pdb-E{Z<>"modnum"Z<>} | 
| 369 |  |  |  |  |  |  |  | 
| 370 |  |  |  |  |  |  | The database's modification number. An integer. | 
| 371 |  |  |  |  |  |  |  | 
| 372 |  |  |  |  |  |  | =item $pdb-E{Z<>"type"Z<>} | 
| 373 |  |  |  |  |  |  |  | 
| 374 |  |  |  |  |  |  | The database's type. A four-character string. | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | =item $pdb-E{Z<>"creator"Z<>} | 
| 377 |  |  |  |  |  |  |  | 
| 378 |  |  |  |  |  |  | The database's creator. A four-character string. | 
| 379 |  |  |  |  |  |  |  | 
| 380 |  |  |  |  |  |  | =item $pdb-E{Z<>"uniqueIDseed"Z<>} | 
| 381 |  |  |  |  |  |  |  | 
| 382 |  |  |  |  |  |  | The database's unique ID seed. An integer. | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | =item $pdb-E{Z<>"2NULs"Z<>} | 
| 385 |  |  |  |  |  |  |  | 
| 386 |  |  |  |  |  |  | The two NUL bytes that appear after the record index and the AppInfo | 
| 387 |  |  |  |  |  |  | block. Included here because every once in a long while, they are not | 
| 388 |  |  |  |  |  |  | NULs, for some reason. | 
| 389 |  |  |  |  |  |  |  | 
| 390 |  |  |  |  |  |  | =item $pdb-E{Z<>"appinfo"Z<>} | 
| 391 |  |  |  |  |  |  |  | 
| 392 |  |  |  |  |  |  | The AppInfo block, as returned by the $pdb->ParseAppInfoBlock() helper | 
| 393 |  |  |  |  |  |  | method. | 
| 394 |  |  |  |  |  |  |  | 
| 395 |  |  |  |  |  |  | =item $pdb-E{Z<>"sort"Z<>} | 
| 396 |  |  |  |  |  |  |  | 
| 397 |  |  |  |  |  |  | The sort block, as returned by the $pdb->ParseSortBlock() helper | 
| 398 |  |  |  |  |  |  | method. | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | =item @{$pdb-E{Z<>"records"Z<>}} | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | The list of records in the database, as returned by the | 
| 403 |  |  |  |  |  |  | $pdb->ParseRecord() helper method. Resource databases do not have | 
| 404 |  |  |  |  |  |  | this. | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | =item @{$pdb-E{Z<>"resources"Z<>}} | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | The list of resources in the database, as returned by the | 
| 409 |  |  |  |  |  |  | $pdb->ParseResource() helper method. Record databases do not have | 
| 410 |  |  |  |  |  |  | this. | 
| 411 |  |  |  |  |  |  |  | 
| 412 |  |  |  |  |  |  | =back | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | All of these fields may be set by hand, but should conform to the | 
| 415 |  |  |  |  |  |  | format given above. | 
| 416 |  |  |  |  |  |  |  | 
| 417 |  |  |  |  |  |  | =for html | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  |  | 
| 420 |  |  |  |  |  |  | =cut | 
| 421 |  |  |  |  |  |  | #' | 
| 422 |  |  |  |  |  |  |  | 
| 423 |  |  |  |  |  |  | # Load | 
| 424 |  |  |  |  |  |  | sub Load | 
| 425 |  |  |  |  |  |  | { | 
| 426 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 427 | 0 |  |  |  |  | 0 | my $fname = shift;		# Filename to read from | 
| 428 | 0 |  |  |  |  | 0 | my $buf;			# Buffer into which to read stuff | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  | # Open database file | 
| 431 | 0 | 0 |  |  |  | 0 | open PDB, "< $fname" or die "Can't open \"$fname\": $!\n"; | 
| 432 | 0 |  |  |  |  | 0 | binmode PDB;			# Read as binary file under MS-DOS | 
| 433 |  |  |  |  |  |  |  | 
| 434 |  |  |  |  |  |  | # Get the size of the file. It'll be useful later | 
| 435 | 0 |  |  |  |  | 0 | seek PDB, 0, 2;		# 2 == SEEK_END. Seek to the end. | 
| 436 | 0 |  |  |  |  | 0 | $self->{_size} = tell PDB; | 
| 437 | 0 |  |  |  |  | 0 | seek PDB, 0, 0;		# 0 == SEEK_START. Rewind to the beginning. | 
| 438 |  |  |  |  |  |  |  | 
| 439 |  |  |  |  |  |  | # Read header | 
| 440 | 0 |  |  |  |  | 0 | my $name; | 
| 441 |  |  |  |  |  |  | my $attributes; | 
| 442 | 0 |  |  |  |  | 0 | my $version; | 
| 443 | 0 |  |  |  |  | 0 | my $ctime; | 
| 444 | 0 |  |  |  |  | 0 | my $mtime; | 
| 445 | 0 |  |  |  |  | 0 | my $baktime; | 
| 446 | 0 |  |  |  |  | 0 | my $modnum; | 
| 447 | 0 |  |  |  |  | 0 | my $appinfo_offset; | 
| 448 | 0 |  |  |  |  | 0 | my $sort_offset; | 
| 449 | 0 |  |  |  |  | 0 | my $type; | 
| 450 | 0 |  |  |  |  | 0 | my $creator; | 
| 451 | 0 |  |  |  |  | 0 | my $uniqueIDseed; | 
| 452 |  |  |  |  |  |  |  | 
| 453 | 0 |  |  |  |  | 0 | read PDB, $buf, $HeaderLen;	# Read the PDB header | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | # Split header into its component fields | 
| 456 | 0 |  |  |  |  | 0 | ($name, $attributes, $version, $ctime, $mtime, $baktime, | 
| 457 |  |  |  |  |  |  | $modnum, $appinfo_offset, $sort_offset, $type, $creator, | 
| 458 |  |  |  |  |  |  | $uniqueIDseed) = | 
| 459 |  |  |  |  |  |  | unpack "a32 n n N N N N N N a4 a4 N", $buf; | 
| 460 |  |  |  |  |  |  |  | 
| 461 | 0 |  |  |  |  | 0 | ($self->{name} = $name) =~ s/\0.*$//; | 
| 462 | 0 | 0 |  |  |  | 0 | $self->{attributes}{resource} = 1 if $attributes & 0x0001; | 
| 463 | 0 | 0 |  |  |  | 0 | $self->{attributes}{"read-only"} = 1 if $attributes & 0x0002; | 
| 464 | 0 | 0 |  |  |  | 0 | $self->{attributes}{"AppInfo dirty"} = 1 if $attributes & 0x0004; | 
| 465 | 0 | 0 |  |  |  | 0 | $self->{attributes}{backup} = 1 if $attributes & 0x0008; | 
| 466 | 0 | 0 |  |  |  | 0 | $self->{attributes}{"OK newer"} = 1 if $attributes & 0x0010; | 
| 467 | 0 | 0 |  |  |  | 0 | $self->{attributes}{reset} = 1 if $attributes & 0x0020; | 
| 468 | 0 | 0 |  |  |  | 0 | $self->{attributes}{open} = 1 if $attributes & 0x8000; | 
| 469 | 0 | 0 |  |  |  | 0 | $self->{attributes}{launchable} = 1 if $attributes & 0x0200; | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  | # Attribute names as of PalmOS 5.0 ( see /Core/System/DataMgr.h ) | 
| 472 |  |  |  |  |  |  |  | 
| 473 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'ResDB'}			= 1 if $attributes & 0x0001; | 
| 474 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'ReadOnly'}		= 1 if $attributes & 0x0002; | 
| 475 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'AppInfoDirty'}		= 1 if $attributes & 0x0004; | 
| 476 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'Backup'}			= 1 if $attributes & 0x0008; | 
| 477 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'OKToInstallNewer'}	= 1 if $attributes & 0x0010; | 
| 478 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'ResetAfterInstall'}	= 1 if $attributes & 0x0020; | 
| 479 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'CopyPrevention'}		= 1 if $attributes & 0x0040; | 
| 480 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'Stream'}			= 1 if $attributes & 0x0080; | 
| 481 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'Hidden'}			= 1 if $attributes & 0x0100; | 
| 482 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'LaunchableData'}		= 1 if $attributes & 0x0200; | 
| 483 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'Recyclable'}		= 1 if $attributes & 0x0400; | 
| 484 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'Bundle'}			= 1 if $attributes & 0x0800; | 
| 485 | 0 | 0 |  |  |  | 0 | $self->{'attributes'}{'Open'}			= 1 if $attributes & 0x8000; | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  |  | 
| 488 | 0 |  |  |  |  | 0 | $self->{version} = $version; | 
| 489 | 0 |  |  |  |  | 0 | $self->{ctime} = $ctime - $EPOCH_1904; | 
| 490 | 0 |  |  |  |  | 0 | $self->{mtime} = $mtime - $EPOCH_1904; | 
| 491 | 0 |  |  |  |  | 0 | $self->{baktime} = $baktime - $EPOCH_1904; | 
| 492 | 0 |  |  |  |  | 0 | $self->{modnum} = $modnum; | 
| 493 |  |  |  |  |  |  | # _appinfo_offset and _sort_offset are private fields | 
| 494 | 0 |  |  |  |  | 0 | $self->{_appinfo_offset} = $appinfo_offset; | 
| 495 | 0 |  |  |  |  | 0 | $self->{_sort_offset} = $sort_offset; | 
| 496 | 0 |  |  |  |  | 0 | $self->{type} = $type; | 
| 497 | 0 |  |  |  |  | 0 | $self->{creator} = $creator; | 
| 498 | 0 |  |  |  |  | 0 | $self->{uniqueIDseed} = $uniqueIDseed; | 
| 499 |  |  |  |  |  |  |  | 
| 500 |  |  |  |  |  |  | # Rebless this PDB object, depending on its type and/or | 
| 501 |  |  |  |  |  |  | # creator. This allows us to magically invoke the proper | 
| 502 |  |  |  |  |  |  | # &Parse*() function on the various parts of the database. | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | # Look for most specific handlers first, least specific ones | 
| 505 |  |  |  |  |  |  | # last. That is, first look for a handler that deals | 
| 506 |  |  |  |  |  |  | # specifically with this database's creator and type, then for | 
| 507 |  |  |  |  |  |  | # one that deals with this database's creator and any type, | 
| 508 |  |  |  |  |  |  | # and finally for one that deals with anything. | 
| 509 |  |  |  |  |  |  |  | 
| 510 | 0 |  |  |  |  | 0 | my $handler; | 
| 511 | 0 | 0 | 0 |  |  | 0 | if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) | 
| 512 |  |  |  |  |  |  | { | 
| 513 |  |  |  |  |  |  | # Look among resource handlers | 
| 514 |  |  |  |  |  |  | $handler = $PRCHandlers{$self->{creator}}{$self->{type}} || | 
| 515 |  |  |  |  |  |  | $PRCHandlers{undef}{$self->{type}} || | 
| 516 |  |  |  |  |  |  | $PRCHandlers{$self->{creator}}{""} || | 
| 517 | 0 |  | 0 |  |  | 0 | $PRCHandlers{""}{""}; | 
| 518 |  |  |  |  |  |  | } else { | 
| 519 |  |  |  |  |  |  | # Look among record handlers | 
| 520 |  |  |  |  |  |  | $handler = $PDBHandlers{$self->{creator}}{$self->{type}} || | 
| 521 |  |  |  |  |  |  | $PDBHandlers{""}{$self->{type}} || | 
| 522 |  |  |  |  |  |  | $PDBHandlers{$self->{creator}}{""} || | 
| 523 | 0 |  | 0 |  |  | 0 | $PDBHandlers{""}{""}; | 
| 524 |  |  |  |  |  |  | } | 
| 525 |  |  |  |  |  |  |  | 
| 526 | 0 | 0 |  |  |  | 0 | if (defined($handler)) | 
| 527 |  |  |  |  |  |  | { | 
| 528 | 0 |  |  |  |  | 0 | bless $self, $handler; | 
| 529 |  |  |  |  |  |  | } else { | 
| 530 |  |  |  |  |  |  | # XXX - This should probably return 'undef' or something, | 
| 531 |  |  |  |  |  |  | # rather than die. | 
| 532 | 0 |  |  |  |  | 0 | die "No handler defined for creator \"$creator\", type \"$type\"\n"; | 
| 533 |  |  |  |  |  |  | } | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | ## Read record/resource index | 
| 536 |  |  |  |  |  |  | # Read index header | 
| 537 | 0 |  |  |  |  | 0 | read PDB, $buf, $RecIndexHeaderLen; | 
| 538 |  |  |  |  |  |  |  | 
| 539 | 0 |  |  |  |  | 0 | my $next_index; | 
| 540 |  |  |  |  |  |  | my $numrecs; | 
| 541 |  |  |  |  |  |  |  | 
| 542 | 0 |  |  |  |  | 0 | ($next_index, $numrecs) = unpack "N n", $buf; | 
| 543 | 0 |  |  |  |  | 0 | $self->{_numrecs} = $numrecs; | 
| 544 |  |  |  |  |  |  |  | 
| 545 |  |  |  |  |  |  | # Read the index itself | 
| 546 | 0 | 0 | 0 |  |  | 0 | if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) | 
| 547 |  |  |  |  |  |  | { | 
| 548 | 0 |  |  |  |  | 0 | &_load_rsrc_index($self, \*PDB); | 
| 549 |  |  |  |  |  |  | } else { | 
| 550 | 0 |  |  |  |  | 0 | &_load_rec_index($self, \*PDB); | 
| 551 |  |  |  |  |  |  | } | 
| 552 |  |  |  |  |  |  |  | 
| 553 |  |  |  |  |  |  | # Read the two NUL bytes | 
| 554 |  |  |  |  |  |  | # XXX - Actually, these are bogus. They don't appear in the | 
| 555 |  |  |  |  |  |  | # spec. The Right Thing to do is to ignore them, and use the | 
| 556 |  |  |  |  |  |  | # specified or calculated offsets, if they're sane. Sane == | 
| 557 |  |  |  |  |  |  | # appears later than the current position. | 
| 558 |  |  |  |  |  |  | #	read PDB, $buf, 2; | 
| 559 |  |  |  |  |  |  | #	$self->{"2NULs"} = $buf; | 
| 560 |  |  |  |  |  |  |  | 
| 561 |  |  |  |  |  |  | # Read AppInfo block, if it exists | 
| 562 | 0 | 0 |  |  |  | 0 | if ($self->{_appinfo_offset} != 0) | 
| 563 |  |  |  |  |  |  | { | 
| 564 | 0 |  |  |  |  | 0 | &_load_appinfo_block($self, \*PDB); | 
| 565 |  |  |  |  |  |  | } | 
| 566 |  |  |  |  |  |  |  | 
| 567 |  |  |  |  |  |  | # Read sort block, if it exists | 
| 568 | 0 | 0 |  |  |  | 0 | if ($self->{_sort_offset} != 0) | 
| 569 |  |  |  |  |  |  | { | 
| 570 | 0 |  |  |  |  | 0 | &_load_sort_block($self, \*PDB); | 
| 571 |  |  |  |  |  |  | } | 
| 572 |  |  |  |  |  |  |  | 
| 573 |  |  |  |  |  |  | # Read record/resource list | 
| 574 | 0 | 0 | 0 |  |  | 0 | if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) | 
| 575 |  |  |  |  |  |  | { | 
| 576 | 0 |  |  |  |  | 0 | &_load_resources($self, \*PDB); | 
| 577 |  |  |  |  |  |  | } else { | 
| 578 | 0 |  |  |  |  | 0 | &_load_records($self, \*PDB); | 
| 579 |  |  |  |  |  |  | } | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | # These keys were needed for parsing the file, but are not | 
| 582 |  |  |  |  |  |  | # needed any longer. Delete them. | 
| 583 | 0 |  |  |  |  | 0 | delete $self->{_index}; | 
| 584 | 0 |  |  |  |  | 0 | delete $self->{_numrecs}; | 
| 585 | 0 |  |  |  |  | 0 | delete $self->{_appinfo_offset}; | 
| 586 | 0 |  |  |  |  | 0 | delete $self->{_sort_offset}; | 
| 587 | 0 |  |  |  |  | 0 | delete $self->{_size}; | 
| 588 |  |  |  |  |  |  |  | 
| 589 | 0 |  |  |  |  | 0 | close PDB; | 
| 590 |  |  |  |  |  |  | } | 
| 591 |  |  |  |  |  |  |  | 
| 592 |  |  |  |  |  |  | # _load_rec_index | 
| 593 |  |  |  |  |  |  | # Private function. Read the record index, for a record database | 
| 594 |  |  |  |  |  |  | sub _load_rec_index | 
| 595 |  |  |  |  |  |  | { | 
| 596 | 0 |  |  | 0 |  | 0 | my $pdb = shift; | 
| 597 | 0 |  |  |  |  | 0 | my $fh = shift;		# Input file handle | 
| 598 | 0 |  |  |  |  | 0 | my $i; | 
| 599 | 0 |  |  |  |  | 0 | my $lastoffset = 0; | 
| 600 |  |  |  |  |  |  |  | 
| 601 |  |  |  |  |  |  | # Read each record index entry in turn | 
| 602 | 0 |  |  |  |  | 0 | for ($i = 0; $i < $pdb->{_numrecs}; $i++) | 
| 603 |  |  |  |  |  |  | { | 
| 604 | 0 |  |  |  |  | 0 | my $buf;		# Input buffer | 
| 605 |  |  |  |  |  |  |  | 
| 606 |  |  |  |  |  |  | # Read the next record index entry | 
| 607 |  |  |  |  |  |  | my $offset; | 
| 608 | 0 |  |  |  |  | 0 | my $attributes; | 
| 609 | 0 |  |  |  |  | 0 | my @id;			# Raw ID | 
| 610 | 0 |  |  |  |  | 0 | my $id;			# Numerical ID | 
| 611 | 0 |  |  |  |  | 0 | my $entry = {};		# Parsed index entry | 
| 612 |  |  |  |  |  |  |  | 
| 613 | 0 |  |  |  |  | 0 | read $fh, $buf, $IndexRecLen; | 
| 614 |  |  |  |  |  |  |  | 
| 615 |  |  |  |  |  |  | # The ID field is a bit weird: it's represented as 3 | 
| 616 |  |  |  |  |  |  | # bytes, but it's really a double word (long) value. | 
| 617 |  |  |  |  |  |  |  | 
| 618 | 0 |  |  |  |  | 0 | ($offset, $attributes, @id) = unpack "N C C3", $buf; | 
| 619 |  |  |  |  |  |  |  | 
| 620 | 0 | 0 |  |  |  | 0 | if ($offset == $lastoffset) | 
| 621 |  |  |  |  |  |  | { | 
| 622 | 0 |  |  |  |  | 0 | print STDERR "Record $i has same offset as previous one: $offset\n"; | 
| 623 |  |  |  |  |  |  | } | 
| 624 |  |  |  |  |  |  |  | 
| 625 | 0 |  |  |  |  | 0 | $lastoffset = $offset; | 
| 626 |  |  |  |  |  |  |  | 
| 627 | 0 |  |  |  |  | 0 | $entry->{offset} = $offset; | 
| 628 |  |  |  |  |  |  |  | 
| 629 | 0 | 0 |  |  |  | 0 | $entry->{attributes}{expunged} = 1 if $attributes & 0x80; | 
| 630 | 0 | 0 |  |  |  | 0 | $entry->{attributes}{dirty} = 1 if $attributes & 0x40; | 
| 631 | 0 | 0 |  |  |  | 0 | $entry->{attributes}{deleted} = 1 if $attributes & 0x20; | 
| 632 | 0 | 0 |  |  |  | 0 | $entry->{attributes}{private} = 1 if $attributes & 0x10; | 
| 633 |  |  |  |  |  |  |  | 
| 634 |  |  |  |  |  |  | # Attribute names as of PalmOS 5.0 ( see /Core/System/DataMgr.h ) | 
| 635 |  |  |  |  |  |  |  | 
| 636 | 0 | 0 |  |  |  | 0 | $entry->{'attributes'}{'Delete'}	= 1 if $attributes & 0x80; | 
| 637 | 0 | 0 |  |  |  | 0 | $entry->{'attributes'}{'Dirty'}		= 1 if $attributes & 0x40; | 
| 638 | 0 | 0 |  |  |  | 0 | $entry->{'attributes'}{'Busy'}		= 1 if $attributes & 0x20; | 
| 639 | 0 | 0 |  |  |  | 0 | $entry->{'attributes'}{'Secret'}	= 1 if $attributes & 0x10; | 
| 640 |  |  |  |  |  |  |  | 
| 641 | 0 |  |  |  |  | 0 | $entry->{id} =	($id[0] << 16) | | 
| 642 |  |  |  |  |  |  | ($id[1] << 8)  | | 
| 643 |  |  |  |  |  |  | $id[2]; | 
| 644 |  |  |  |  |  |  |  | 
| 645 |  |  |  |  |  |  | # The lower 4 bits of the attributes field are | 
| 646 |  |  |  |  |  |  | # overloaded: If the record has been deleted and/or | 
| 647 |  |  |  |  |  |  | # expunged, then bit 0x08 indicates whether the record | 
| 648 |  |  |  |  |  |  | # should be archived. Otherwise (if it's an ordinary, | 
| 649 |  |  |  |  |  |  | # non-deleted record), the lower 4 bits specify the | 
| 650 |  |  |  |  |  |  | # category that the record belongs in. | 
| 651 | 0 | 0 |  |  |  | 0 | if (($attributes & 0xa0) == 0) | 
| 652 |  |  |  |  |  |  | { | 
| 653 | 0 |  |  |  |  | 0 | $entry->{category} = $attributes & 0x0f; | 
| 654 |  |  |  |  |  |  | } else { | 
| 655 | 0 | 0 |  |  |  | 0 | $entry->{attributes}{archive} = 1 | 
| 656 |  |  |  |  |  |  | if $attributes & 0x08; | 
| 657 |  |  |  |  |  |  | } | 
| 658 |  |  |  |  |  |  |  | 
| 659 |  |  |  |  |  |  | # Put this information on a temporary array | 
| 660 | 0 |  |  |  |  | 0 | push @{$pdb->{_index}}, $entry; | 
|  | 0 |  |  |  |  | 0 |  | 
| 661 |  |  |  |  |  |  | } | 
| 662 |  |  |  |  |  |  | } | 
| 663 |  |  |  |  |  |  |  | 
| 664 |  |  |  |  |  |  | # _load_rsrc_index | 
| 665 |  |  |  |  |  |  | # Private function. Read the resource index, for a resource database | 
| 666 |  |  |  |  |  |  | sub _load_rsrc_index | 
| 667 |  |  |  |  |  |  | { | 
| 668 | 0 |  |  | 0 |  | 0 | my $pdb = shift; | 
| 669 | 0 |  |  |  |  | 0 | my $fh = shift;		# Input file handle | 
| 670 | 0 |  |  |  |  | 0 | my $i; | 
| 671 |  |  |  |  |  |  |  | 
| 672 |  |  |  |  |  |  | # Read each resource index entry in turn | 
| 673 | 0 |  |  |  |  | 0 | for ($i = 0; $i < $pdb->{_numrecs}; $i++) | 
| 674 |  |  |  |  |  |  | { | 
| 675 | 0 |  |  |  |  | 0 | my $buf;		# Input buffer | 
| 676 |  |  |  |  |  |  |  | 
| 677 |  |  |  |  |  |  | # Read the next resource index entry | 
| 678 |  |  |  |  |  |  | my $type; | 
| 679 | 0 |  |  |  |  | 0 | my $id; | 
| 680 | 0 |  |  |  |  | 0 | my $offset; | 
| 681 | 0 |  |  |  |  | 0 | my $entry = {};		# Parsed index entry | 
| 682 |  |  |  |  |  |  |  | 
| 683 | 0 |  |  |  |  | 0 | read $fh, $buf, $IndexRsrcLen; | 
| 684 |  |  |  |  |  |  |  | 
| 685 | 0 |  |  |  |  | 0 | ($type, $id, $offset) = unpack "a4 n N", $buf; | 
| 686 |  |  |  |  |  |  |  | 
| 687 | 0 |  |  |  |  | 0 | $entry->{type} = $type; | 
| 688 | 0 |  |  |  |  | 0 | $entry->{id} = $id; | 
| 689 | 0 |  |  |  |  | 0 | $entry->{offset} = $offset; | 
| 690 |  |  |  |  |  |  |  | 
| 691 | 0 |  |  |  |  | 0 | push @{$pdb->{_index}}, $entry; | 
|  | 0 |  |  |  |  | 0 |  | 
| 692 |  |  |  |  |  |  | } | 
| 693 |  |  |  |  |  |  | } | 
| 694 |  |  |  |  |  |  |  | 
| 695 |  |  |  |  |  |  | # _load_appinfo_block | 
| 696 |  |  |  |  |  |  | # Private function. Read the AppInfo block | 
| 697 |  |  |  |  |  |  | sub _load_appinfo_block | 
| 698 |  |  |  |  |  |  | { | 
| 699 | 0 |  |  | 0 |  | 0 | my $pdb = shift; | 
| 700 | 0 |  |  |  |  | 0 | my $fh = shift;		# Input file handle | 
| 701 | 0 |  |  |  |  | 0 | my $len;		# Length of AppInfo block | 
| 702 |  |  |  |  |  |  | my $buf;		# Input buffer | 
| 703 |  |  |  |  |  |  |  | 
| 704 |  |  |  |  |  |  | # Sanity check: make sure we're positioned at the beginning of | 
| 705 |  |  |  |  |  |  | # the AppInfo block | 
| 706 | 0 | 0 |  |  |  | 0 | if (tell($fh) > $pdb->{_appinfo_offset}) | 
| 707 |  |  |  |  |  |  | { | 
| 708 |  |  |  |  |  |  | die "Bad AppInfo offset: expected ", | 
| 709 | 0 |  |  |  |  | 0 | sprintf("0x%08x", $pdb->{_appinfo_offset}), | 
| 710 |  |  |  |  |  |  | ", but I'm at ", | 
| 711 |  |  |  |  |  |  | tell($fh), "\n"; | 
| 712 |  |  |  |  |  |  | } | 
| 713 |  |  |  |  |  |  |  | 
| 714 |  |  |  |  |  |  | # Seek to the right place, if necessary | 
| 715 | 0 | 0 |  |  |  | 0 | if (tell($fh) != $pdb->{_appinfo_offset}) | 
| 716 |  |  |  |  |  |  | { | 
| 717 | 0 |  |  |  |  | 0 | seek PDB, $pdb->{_appinfo_offset}, 0; | 
| 718 |  |  |  |  |  |  | } | 
| 719 |  |  |  |  |  |  |  | 
| 720 |  |  |  |  |  |  | # There's nothing that explicitly gives the size of the | 
| 721 |  |  |  |  |  |  | # AppInfo block. Rather, it has to be inferred from the offset | 
| 722 |  |  |  |  |  |  | # of the AppInfo block (previously recorded in | 
| 723 |  |  |  |  |  |  | # $pdb->{_appinfo_offset}) and whatever's next in the file. | 
| 724 |  |  |  |  |  |  | # That's either the sort block, the first data record, or the | 
| 725 |  |  |  |  |  |  | # end of the file. | 
| 726 |  |  |  |  |  |  |  | 
| 727 | 0 | 0 | 0 |  |  | 0 | if ($pdb->{_sort_offset}) | 
|  |  | 0 |  |  |  |  |  | 
| 728 |  |  |  |  |  |  | { | 
| 729 |  |  |  |  |  |  | # The next thing in the file is the sort block | 
| 730 | 0 |  |  |  |  | 0 | $len = $pdb->{_sort_offset} - $pdb->{_appinfo_offset}; | 
| 731 | 0 |  |  |  |  | 0 | } elsif ((defined $pdb->{_index}) && @{$pdb->{_index}}) | 
| 732 |  |  |  |  |  |  | { | 
| 733 |  |  |  |  |  |  | # There's no sort block; the next thing in the file is | 
| 734 |  |  |  |  |  |  | # the first data record | 
| 735 |  |  |  |  |  |  | $len = $pdb->{_index}[0]{offset} - | 
| 736 | 0 |  |  |  |  | 0 | $pdb->{_appinfo_offset}; | 
| 737 |  |  |  |  |  |  | } else { | 
| 738 |  |  |  |  |  |  | # There's no sort block and there are no records. The | 
| 739 |  |  |  |  |  |  | # AppInfo block goes to the end of the file. | 
| 740 | 0 |  |  |  |  | 0 | $len = $pdb->{_size} - $pdb->{_appinfo_offset}; | 
| 741 |  |  |  |  |  |  | } | 
| 742 |  |  |  |  |  |  |  | 
| 743 |  |  |  |  |  |  | # Read the AppInfo block | 
| 744 | 0 |  |  |  |  | 0 | read $fh, $buf, $len; | 
| 745 |  |  |  |  |  |  |  | 
| 746 |  |  |  |  |  |  | # Tell the real class to parse the AppInfo block | 
| 747 | 0 |  |  |  |  | 0 | $pdb->{appinfo} = $pdb->ParseAppInfoBlock($buf); | 
| 748 |  |  |  |  |  |  | } | 
| 749 |  |  |  |  |  |  |  | 
| 750 |  |  |  |  |  |  | # _load_sort_block | 
| 751 |  |  |  |  |  |  | # Private function. Read the sort block. | 
| 752 |  |  |  |  |  |  | sub _load_sort_block | 
| 753 |  |  |  |  |  |  | { | 
| 754 | 0 |  |  | 0 |  | 0 | my $pdb = shift; | 
| 755 | 0 |  |  |  |  | 0 | my $fh = shift;		# Input file handle | 
| 756 | 0 |  |  |  |  | 0 | my $len;		# Length of sort block | 
| 757 |  |  |  |  |  |  | my $buf;		# Input buffer | 
| 758 |  |  |  |  |  |  |  | 
| 759 |  |  |  |  |  |  | # Sanity check: make sure we're positioned at the beginning of | 
| 760 |  |  |  |  |  |  | # the sort block | 
| 761 | 0 | 0 |  |  |  | 0 | if (tell($fh) > $pdb->{_sort_offset}) | 
| 762 |  |  |  |  |  |  | { | 
| 763 |  |  |  |  |  |  | die "Bad sort block offset: expected ", | 
| 764 | 0 |  |  |  |  | 0 | sprintf("0x%08x", $pdb->{_sort_offset}), | 
| 765 |  |  |  |  |  |  | ", but I'm at ", | 
| 766 |  |  |  |  |  |  | tell($fh), "\n"; | 
| 767 |  |  |  |  |  |  | } | 
| 768 |  |  |  |  |  |  |  | 
| 769 |  |  |  |  |  |  | # Seek to the right place, if necessary | 
| 770 | 0 | 0 |  |  |  | 0 | if (tell($fh) != $pdb->{_sort_offset}) | 
| 771 |  |  |  |  |  |  | { | 
| 772 | 0 |  |  |  |  | 0 | seek PDB, $pdb->{_sort_offset}, 0; | 
| 773 |  |  |  |  |  |  | } | 
| 774 |  |  |  |  |  |  |  | 
| 775 |  |  |  |  |  |  | # There's nothing that explicitly gives the size of the sort | 
| 776 |  |  |  |  |  |  | # block. Rather, it has to be inferred from the offset of the | 
| 777 |  |  |  |  |  |  | # sort block (previously recorded in $pdb->{_sort_offset}) | 
| 778 |  |  |  |  |  |  | # and whatever's next in the file. That's either the first | 
| 779 |  |  |  |  |  |  | # data record, or the end of the file. | 
| 780 |  |  |  |  |  |  |  | 
| 781 | 0 | 0 |  |  |  | 0 | if (defined($pdb->{_index})) | 
| 782 |  |  |  |  |  |  | { | 
| 783 |  |  |  |  |  |  | # The next thing in the file is the first data record | 
| 784 |  |  |  |  |  |  | $len = $pdb->{_index}[0]{offset} - | 
| 785 | 0 |  |  |  |  | 0 | $pdb->{_sort_offset}; | 
| 786 |  |  |  |  |  |  | } else { | 
| 787 |  |  |  |  |  |  | # There are no records. The sort block goes to the end | 
| 788 |  |  |  |  |  |  | # of the file. | 
| 789 | 0 |  |  |  |  | 0 | $len = $pdb->{_size} - $pdb->{_sort_offset}; | 
| 790 |  |  |  |  |  |  | } | 
| 791 |  |  |  |  |  |  |  | 
| 792 |  |  |  |  |  |  | # Read the AppInfo block | 
| 793 | 0 |  |  |  |  | 0 | read $fh, $buf, $len; | 
| 794 |  |  |  |  |  |  |  | 
| 795 |  |  |  |  |  |  | # XXX - Check to see if the sort block has some predefined | 
| 796 |  |  |  |  |  |  | # structure. If so, it might be a good idea to parse the sort | 
| 797 |  |  |  |  |  |  | # block here. | 
| 798 |  |  |  |  |  |  |  | 
| 799 |  |  |  |  |  |  | # Tell the real class to parse the sort block | 
| 800 | 0 |  |  |  |  | 0 | $pdb->{sort} = $pdb->ParseSortBlock($buf); | 
| 801 |  |  |  |  |  |  | } | 
| 802 |  |  |  |  |  |  |  | 
| 803 |  |  |  |  |  |  | # _load_records | 
| 804 |  |  |  |  |  |  | # Private function. Load the actual data records, for a record database | 
| 805 |  |  |  |  |  |  | # (PDB) | 
| 806 |  |  |  |  |  |  | sub _load_records | 
| 807 |  |  |  |  |  |  | { | 
| 808 | 0 |  |  | 0 |  | 0 | my $pdb = shift; | 
| 809 | 0 |  |  |  |  | 0 | my $fh = shift;		# Input file handle | 
| 810 | 0 |  |  |  |  | 0 | my $i; | 
| 811 |  |  |  |  |  |  |  | 
| 812 |  |  |  |  |  |  | # Read each record in turn | 
| 813 | 0 |  |  |  |  | 0 | for ($i = 0; $i < $pdb->{_numrecs}; $i++) | 
| 814 |  |  |  |  |  |  | { | 
| 815 | 0 |  |  |  |  | 0 | my $len;	# Length of record | 
| 816 |  |  |  |  |  |  | my $buf;	# Input buffer | 
| 817 |  |  |  |  |  |  |  | 
| 818 |  |  |  |  |  |  | # Sanity check: make sure we're where we think we | 
| 819 |  |  |  |  |  |  | # should be. | 
| 820 | 0 | 0 |  |  |  | 0 | if (tell($fh) > $pdb->{_index}[$i]{offset}) | 
| 821 |  |  |  |  |  |  | { | 
| 822 |  |  |  |  |  |  | die "Bad offset for record $i: expected ", | 
| 823 |  |  |  |  |  |  | sprintf("0x%08x", | 
| 824 | 0 |  |  |  |  | 0 | $pdb->{_index}[$i]{offset}), | 
| 825 |  |  |  |  |  |  | " but it's at ", | 
| 826 |  |  |  |  |  |  | sprintf("[0x%08x]", tell($fh)), "\n"; | 
| 827 |  |  |  |  |  |  | } | 
| 828 |  |  |  |  |  |  |  | 
| 829 |  |  |  |  |  |  | # Seek to the right place, if necessary | 
| 830 | 0 | 0 |  |  |  | 0 | if (tell($fh) != $pdb->{_index}[$i]{offset}) | 
| 831 |  |  |  |  |  |  | { | 
| 832 | 0 |  |  |  |  | 0 | seek PDB, $pdb->{_index}[$i]{offset}, 0; | 
| 833 |  |  |  |  |  |  | } | 
| 834 |  |  |  |  |  |  |  | 
| 835 |  |  |  |  |  |  | # Compute the length of the record: the last record | 
| 836 |  |  |  |  |  |  | # extends to the end of the file. The others extend to | 
| 837 |  |  |  |  |  |  | # the beginning of the next record. | 
| 838 | 0 | 0 |  |  |  | 0 | if ($i == $pdb->{_numrecs} - 1) | 
| 839 |  |  |  |  |  |  | { | 
| 840 |  |  |  |  |  |  | # This is the last record | 
| 841 |  |  |  |  |  |  | $len = $pdb->{_size} - | 
| 842 | 0 |  |  |  |  | 0 | $pdb->{_index}[$i]{offset}; | 
| 843 |  |  |  |  |  |  | } else { | 
| 844 |  |  |  |  |  |  | # This is not the last record | 
| 845 |  |  |  |  |  |  | $len = $pdb->{_index}[$i+1]{offset} - | 
| 846 | 0 |  |  |  |  | 0 | $pdb->{_index}[$i]{offset}; | 
| 847 |  |  |  |  |  |  | } | 
| 848 |  |  |  |  |  |  |  | 
| 849 |  |  |  |  |  |  | # Read the record | 
| 850 | 0 |  |  |  |  | 0 | read $fh, $buf, $len; | 
| 851 |  |  |  |  |  |  |  | 
| 852 |  |  |  |  |  |  | # Tell the real class to parse the record data. Pass | 
| 853 |  |  |  |  |  |  | # &ParseRecord all of the information from the index, | 
| 854 |  |  |  |  |  |  | # plus a "data" field with the raw record data. | 
| 855 | 0 |  |  |  |  | 0 | my $record; | 
| 856 |  |  |  |  |  |  |  | 
| 857 |  |  |  |  |  |  | $record = $pdb->ParseRecord( | 
| 858 | 0 |  |  |  |  | 0 | %{$pdb->{_index}[$i]}, | 
|  | 0 |  |  |  |  | 0 |  | 
| 859 |  |  |  |  |  |  | "data"	=> $buf, | 
| 860 |  |  |  |  |  |  | ); | 
| 861 | 0 |  |  |  |  | 0 | push @{$pdb->{records}}, $record; | 
|  | 0 |  |  |  |  | 0 |  | 
| 862 |  |  |  |  |  |  | } | 
| 863 |  |  |  |  |  |  | } | 
| 864 |  |  |  |  |  |  |  | 
| 865 |  |  |  |  |  |  | # _load_resources | 
| 866 |  |  |  |  |  |  | # Private function. Load the actual data resources, for a resource database | 
| 867 |  |  |  |  |  |  | # (PRC) | 
| 868 |  |  |  |  |  |  | sub _load_resources | 
| 869 |  |  |  |  |  |  | { | 
| 870 | 0 |  |  | 0 |  | 0 | my $pdb = shift; | 
| 871 | 0 |  |  |  |  | 0 | my $fh = shift;		# Input file handle | 
| 872 | 0 |  |  |  |  | 0 | my $i; | 
| 873 |  |  |  |  |  |  |  | 
| 874 |  |  |  |  |  |  | # Read each resource in turn | 
| 875 | 0 |  |  |  |  | 0 | for ($i = 0; $i < $pdb->{_numrecs}; $i++) | 
| 876 |  |  |  |  |  |  | { | 
| 877 | 0 |  |  |  |  | 0 | my $len;	# Length of record | 
| 878 |  |  |  |  |  |  | my $buf;	# Input buffer | 
| 879 |  |  |  |  |  |  |  | 
| 880 |  |  |  |  |  |  | # Sanity check: make sure we're where we think we | 
| 881 |  |  |  |  |  |  | # should be. | 
| 882 | 0 | 0 |  |  |  | 0 | if (tell($fh) > $pdb->{_index}[$i]{offset}) | 
| 883 |  |  |  |  |  |  | { | 
| 884 |  |  |  |  |  |  | die "Bad offset for resource $i: expected ", | 
| 885 |  |  |  |  |  |  | sprintf("0x%08x", | 
| 886 | 0 |  |  |  |  | 0 | $pdb->{_index}[$i]{offset}), | 
| 887 |  |  |  |  |  |  | " but it's at ", | 
| 888 |  |  |  |  |  |  | sprintf("0x%08x", tell($fh)), "\n"; | 
| 889 |  |  |  |  |  |  | } | 
| 890 |  |  |  |  |  |  |  | 
| 891 |  |  |  |  |  |  | # Seek to the right place, if necessary | 
| 892 | 0 | 0 |  |  |  | 0 | if (tell($fh) != $pdb->{_index}[$i]{offset}) | 
| 893 |  |  |  |  |  |  | { | 
| 894 | 0 |  |  |  |  | 0 | seek PDB, $pdb->{_index}[$i]{offset}, 0; | 
| 895 |  |  |  |  |  |  | } | 
| 896 |  |  |  |  |  |  |  | 
| 897 |  |  |  |  |  |  | # Compute the length of the resource: the last | 
| 898 |  |  |  |  |  |  | # resource extends to the end of the file. The others | 
| 899 |  |  |  |  |  |  | # extend to the beginning of the next resource. | 
| 900 | 0 | 0 |  |  |  | 0 | if ($i == $pdb->{_numrecs} - 1) | 
| 901 |  |  |  |  |  |  | { | 
| 902 |  |  |  |  |  |  | # This is the last resource | 
| 903 |  |  |  |  |  |  | $len = $pdb->{_size} - | 
| 904 | 0 |  |  |  |  | 0 | $pdb->{_index}[$i]{offset}; | 
| 905 |  |  |  |  |  |  | } else { | 
| 906 |  |  |  |  |  |  | # This is not the last resource | 
| 907 |  |  |  |  |  |  | $len = $pdb->{_index}[$i+1]{offset} - | 
| 908 | 0 |  |  |  |  | 0 | $pdb->{_index}[$i]{offset}; | 
| 909 |  |  |  |  |  |  | } | 
| 910 |  |  |  |  |  |  |  | 
| 911 |  |  |  |  |  |  | # Read the resource | 
| 912 | 0 |  |  |  |  | 0 | read $fh, $buf, $len; | 
| 913 |  |  |  |  |  |  |  | 
| 914 |  |  |  |  |  |  | # Tell the real class to parse the resource data. Pass | 
| 915 |  |  |  |  |  |  | # &ParseResource all of the information from the | 
| 916 |  |  |  |  |  |  | # index, plus a "data" field with the raw resource | 
| 917 |  |  |  |  |  |  | # data. | 
| 918 | 0 |  |  |  |  | 0 | my $resource; | 
| 919 |  |  |  |  |  |  |  | 
| 920 |  |  |  |  |  |  | $resource = $pdb->ParseResource( | 
| 921 | 0 |  |  |  |  | 0 | %{$pdb->{_index}[$i]}, | 
|  | 0 |  |  |  |  | 0 |  | 
| 922 |  |  |  |  |  |  | "data"	=> $buf, | 
| 923 |  |  |  |  |  |  | ); | 
| 924 | 0 |  |  |  |  | 0 | push @{$pdb->{resources}}, $resource; | 
|  | 0 |  |  |  |  | 0 |  | 
| 925 |  |  |  |  |  |  | } | 
| 926 |  |  |  |  |  |  | } | 
| 927 |  |  |  |  |  |  |  | 
| 928 |  |  |  |  |  |  | =head2 Write | 
| 929 |  |  |  |  |  |  |  | 
| 930 |  |  |  |  |  |  | $pdb->Write("filename"); | 
| 931 |  |  |  |  |  |  |  | 
| 932 |  |  |  |  |  |  | Invokes methods in helper classes to get the application-specific | 
| 933 |  |  |  |  |  |  | parts of the database, then writes the database to the file | 
| 934 |  |  |  |  |  |  | I. | 
| 935 |  |  |  |  |  |  |  | 
| 936 |  |  |  |  |  |  | Write() uses the following helper methods: | 
| 937 |  |  |  |  |  |  |  | 
| 938 |  |  |  |  |  |  | =over | 
| 939 |  |  |  |  |  |  |  | 
| 940 |  |  |  |  |  |  | =item Z<> | 
| 941 |  |  |  |  |  |  |  | 
| 942 |  |  |  |  |  |  | PackAppInfoBlock() | 
| 943 |  |  |  |  |  |  |  | 
| 944 |  |  |  |  |  |  | =item Z<> | 
| 945 |  |  |  |  |  |  |  | 
| 946 |  |  |  |  |  |  | PackSortBlock() | 
| 947 |  |  |  |  |  |  |  | 
| 948 |  |  |  |  |  |  | =item Z<> | 
| 949 |  |  |  |  |  |  |  | 
| 950 |  |  |  |  |  |  | PackResource() or PackRecord() | 
| 951 |  |  |  |  |  |  |  | 
| 952 |  |  |  |  |  |  | =back | 
| 953 |  |  |  |  |  |  |  | 
| 954 |  |  |  |  |  |  | =for html | 
| 955 |  |  |  |  |  |  |  | 
| 956 |  |  |  |  |  |  |  | 
| 957 |  |  |  |  |  |  | See also L. | 
| 958 |  |  |  |  |  |  |  | 
| 959 |  |  |  |  |  |  | =cut | 
| 960 |  |  |  |  |  |  | #'	<-- For Emacs | 
| 961 |  |  |  |  |  |  |  | 
| 962 |  |  |  |  |  |  | sub Write | 
| 963 |  |  |  |  |  |  | { | 
| 964 | 3 |  |  | 3 | 1 | 10 | my $self = shift; | 
| 965 | 3 |  |  |  |  | 7 | my $fname = shift;		# Output file name | 
| 966 | 3 |  |  |  |  | 4 | my @record_data; | 
| 967 |  |  |  |  |  |  |  | 
| 968 |  |  |  |  |  |  | # Open file | 
| 969 | 3 | 50 |  |  |  | 262 | open my $OFILE, '>', $fname or die "Can't write to \"$fname\": $!\n"; | 
| 970 | 3 |  |  |  |  | 10 | binmode $OFILE;			# Write as binary file under MS-DOS | 
| 971 |  |  |  |  |  |  |  | 
| 972 |  |  |  |  |  |  | # Get AppInfo block | 
| 973 | 3 |  |  |  |  | 32 | my $appinfo_block = $self->PackAppInfoBlock; | 
| 974 |  |  |  |  |  |  |  | 
| 975 |  |  |  |  |  |  | # Get sort block | 
| 976 | 3 |  |  |  |  | 22 | my $sort_block = $self->PackSortBlock; | 
| 977 |  |  |  |  |  |  |  | 
| 978 | 3 |  |  |  |  | 6 | my $index_len; | 
| 979 |  |  |  |  |  |  |  | 
| 980 |  |  |  |  |  |  | # Get records or resources | 
| 981 | 3 | 50 | 33 |  |  | 26 | if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) | 
| 982 |  |  |  |  |  |  | { | 
| 983 |  |  |  |  |  |  | # Resource database | 
| 984 | 0 |  |  |  |  | 0 | my $resource; | 
| 985 |  |  |  |  |  |  |  | 
| 986 | 0 |  |  |  |  | 0 | foreach $resource (@{$self->{resources}}) | 
|  | 0 |  |  |  |  | 0 |  | 
| 987 |  |  |  |  |  |  | { | 
| 988 | 0 |  |  |  |  | 0 | my $type; | 
| 989 |  |  |  |  |  |  | my $id; | 
| 990 | 0 |  |  |  |  | 0 | my $data; | 
| 991 |  |  |  |  |  |  |  | 
| 992 |  |  |  |  |  |  | # Get all the stuff that goes in the index, as | 
| 993 |  |  |  |  |  |  | # well as the resource data. | 
| 994 | 0 |  |  |  |  | 0 | $type = $resource->{type}; | 
| 995 | 0 |  |  |  |  | 0 | $id = $resource->{id}; | 
| 996 | 0 |  |  |  |  | 0 | $data = $self->PackResource($resource); | 
| 997 |  |  |  |  |  |  |  | 
| 998 | 0 |  |  |  |  | 0 | push @record_data, [ $type, $id, $data ]; | 
| 999 |  |  |  |  |  |  | } | 
| 1000 |  |  |  |  |  |  | # Figure out size of index | 
| 1001 | 0 |  |  |  |  | 0 | $index_len = $RecIndexHeaderLen + | 
| 1002 |  |  |  |  |  |  | ($#record_data + 1) * $IndexRsrcLen; | 
| 1003 |  |  |  |  |  |  | } else { | 
| 1004 | 3 |  |  |  |  | 5 | my $record; | 
| 1005 |  |  |  |  |  |  |  | 
| 1006 | 3 |  |  |  |  | 4 | foreach $record (@{$self->{records}}) | 
|  | 3 |  |  |  |  | 10 |  | 
| 1007 |  |  |  |  |  |  | { | 
| 1008 | 6 |  |  |  |  | 9 | my $attributes; | 
| 1009 |  |  |  |  |  |  | my $id; | 
| 1010 | 0 |  |  |  |  | 0 | my $data; | 
| 1011 |  |  |  |  |  |  |  | 
| 1012 |  |  |  |  |  |  | # XXX - Should probably check the length of this | 
| 1013 |  |  |  |  |  |  | # record and not add it to the record if it's 0. | 
| 1014 |  |  |  |  |  |  |  | 
| 1015 |  |  |  |  |  |  | # Get all the stuff that goes in the index, as | 
| 1016 |  |  |  |  |  |  | # well as the record data. | 
| 1017 | 6 |  |  |  |  | 7 | $attributes = 0; | 
| 1018 | 6 | 50 | 33 |  |  | 37 | if ($record->{attributes}{expunged} || | 
| 1019 |  |  |  |  |  |  | $record->{attributes}{deleted}) | 
| 1020 |  |  |  |  |  |  | { | 
| 1021 |  |  |  |  |  |  | $attributes |= 0x08 | 
| 1022 | 0 | 0 |  |  |  | 0 | if $record->{attributes}{archive}; | 
| 1023 |  |  |  |  |  |  | } else { | 
| 1024 | 6 |  |  |  |  | 13 | $attributes = ($record->{category} & 0x0f); | 
| 1025 |  |  |  |  |  |  | } | 
| 1026 |  |  |  |  |  |  | $attributes |= 0x80 | 
| 1027 | 6 | 50 |  |  |  | 14 | if $record->{attributes}{expunged}; | 
| 1028 |  |  |  |  |  |  | $attributes |= 0x40 | 
| 1029 | 6 | 50 |  |  |  | 16 | if $record->{attributes}{dirty}; | 
| 1030 |  |  |  |  |  |  | $attributes |= 0x20 | 
| 1031 | 6 | 50 |  |  |  | 16 | if $record->{attributes}{deleted}; | 
| 1032 |  |  |  |  |  |  | $attributes |= 0x10 | 
| 1033 | 6 | 50 |  |  |  | 12 | if $record->{attributes}{private}; | 
| 1034 |  |  |  |  |  |  |  | 
| 1035 | 6 | 50 |  |  |  | 16 | $attributes |= 0x80 if $record->{'attributes'}{'Delete'}; | 
| 1036 | 6 | 50 |  |  |  | 19 | $attributes |= 0x40 if $record->{'attributes'}{'Dirty'}; | 
| 1037 | 6 | 50 |  |  |  | 15 | $attributes |= 0x20 if $record->{'attributes'}{'Busy'}; | 
| 1038 | 6 | 50 |  |  |  | 19 | $attributes |= 0x10 if $record->{'attributes'}{'Secret'}; | 
| 1039 |  |  |  |  |  |  |  | 
| 1040 | 6 |  |  |  |  | 10 | $id = $record->{id}; | 
| 1041 |  |  |  |  |  |  |  | 
| 1042 | 6 |  |  |  |  | 23 | $data = $self->PackRecord($record); | 
| 1043 |  |  |  |  |  |  |  | 
| 1044 | 6 |  |  |  |  | 19 | push @record_data, [ $attributes, $id, $data ]; | 
| 1045 |  |  |  |  |  |  | } | 
| 1046 |  |  |  |  |  |  | # Figure out size of index | 
| 1047 | 3 |  |  |  |  | 8 | $index_len = $RecIndexHeaderLen + | 
| 1048 |  |  |  |  |  |  | ($#record_data + 1) * $IndexRecLen; | 
| 1049 |  |  |  |  |  |  | } | 
| 1050 |  |  |  |  |  |  |  | 
| 1051 | 3 |  |  |  |  | 6 | my $header; | 
| 1052 | 3 |  |  |  |  | 4 | my $attributes = 0x0000; | 
| 1053 | 3 |  |  |  |  | 4 | my $appinfo_offset; | 
| 1054 |  |  |  |  |  |  | my $sort_offset; | 
| 1055 |  |  |  |  |  |  |  | 
| 1056 |  |  |  |  |  |  | # Build attributes field | 
| 1057 |  |  |  |  |  |  | $attributes = | 
| 1058 |  |  |  |  |  |  | ($self->{attributes}{resource}	? 0x0001 : 0) | | 
| 1059 |  |  |  |  |  |  | ($self->{attributes}{"read-only"}	? 0x0002 : 0) | | 
| 1060 |  |  |  |  |  |  | ($self->{attributes}{"AppInfo dirty"}	? 0x0004 : 0) | | 
| 1061 |  |  |  |  |  |  | ($self->{attributes}{backup}	? 0x0008 : 0) | | 
| 1062 |  |  |  |  |  |  | ($self->{attributes}{"OK newer"}	? 0x0010 : 0) | | 
| 1063 |  |  |  |  |  |  | ($self->{attributes}{reset}		? 0x0020 : 0) | | 
| 1064 | 3 | 50 |  |  |  | 47 | ($self->{attributes}{open}		? 0x8000 : 0); | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 1065 |  |  |  |  |  |  |  | 
| 1066 | 3 | 50 |  |  |  | 10 | $attributes |= 0x0001 if $self->{'attributes'}{'ResDB'}; | 
| 1067 | 3 | 50 |  |  |  | 8 | $attributes |= 0x0002 if $self->{'attributes'}{'ReadOnly'}; | 
| 1068 | 3 | 50 |  |  |  | 9 | $attributes |= 0x0004 if $self->{'attributes'}{'AppInfoDirty'}; | 
| 1069 | 3 | 50 |  |  |  | 990 | $attributes |= 0x0008 if $self->{'attributes'}{'Backup'}; | 
| 1070 | 3 | 50 |  |  |  | 9 | $attributes |= 0x0010 if $self->{'attributes'}{'OKToInstallNewer'}; | 
| 1071 | 3 | 50 |  |  |  | 12 | $attributes |= 0x0020 if $self->{'attributes'}{'ResetAfterInstall'}; | 
| 1072 | 3 | 50 |  |  |  | 8 | $attributes |= 0x0040 if $self->{'attributes'}{'CopyPrevention'}; | 
| 1073 | 3 | 50 |  |  |  | 6 | $attributes |= 0x0080 if $self->{'attributes'}{'Stream'}; | 
| 1074 | 3 | 50 |  |  |  | 13 | $attributes |= 0x0100 if $self->{'attributes'}{'Hidden'}; | 
| 1075 | 3 | 50 |  |  |  | 8 | $attributes |= 0x0200 if $self->{'attributes'}{'LaunchableData'}; | 
| 1076 | 3 | 50 |  |  |  | 8 | $attributes |= 0x0400 if $self->{'attributes'}{'Recyclable'}; | 
| 1077 | 3 | 50 |  |  |  | 8 | $attributes |= 0x0800 if $self->{'attributes'}{'Bundle'}; | 
| 1078 | 3 | 50 |  |  |  | 6 | $attributes |= 0x8000 if $self->{'attributes'}{'Open'}; | 
| 1079 |  |  |  |  |  |  |  | 
| 1080 |  |  |  |  |  |  |  | 
| 1081 |  |  |  |  |  |  | # Calculate AppInfo block offset | 
| 1082 | 3 | 50 | 33 |  |  | 12 | if ((!defined($appinfo_block)) || ($appinfo_block eq "")) | 
| 1083 |  |  |  |  |  |  | { | 
| 1084 |  |  |  |  |  |  | # There's no AppInfo block | 
| 1085 | 3 |  |  |  |  | 5 | $appinfo_offset = 0; | 
| 1086 |  |  |  |  |  |  | } else { | 
| 1087 |  |  |  |  |  |  | # Offset of AppInfo block from start of file | 
| 1088 | 0 |  |  |  |  | 0 | $appinfo_offset = $HeaderLen + $index_len + 2; | 
| 1089 |  |  |  |  |  |  | } | 
| 1090 |  |  |  |  |  |  |  | 
| 1091 |  |  |  |  |  |  | # Calculate sort block offset | 
| 1092 | 3 | 50 | 33 |  |  | 11 | if ((!defined($sort_block)) || ($sort_block eq "")) | 
| 1093 |  |  |  |  |  |  | { | 
| 1094 |  |  |  |  |  |  | # There's no sort block | 
| 1095 | 3 |  |  |  |  | 4 | $sort_offset = 0; | 
| 1096 |  |  |  |  |  |  | } else { | 
| 1097 |  |  |  |  |  |  | # Offset of sort block... | 
| 1098 | 0 | 0 |  |  |  | 0 | if ($appinfo_offset == 0) | 
| 1099 |  |  |  |  |  |  | { | 
| 1100 |  |  |  |  |  |  | # ...from start of file | 
| 1101 | 0 |  |  |  |  | 0 | $sort_offset = $HeaderLen + $index_len + 2; | 
| 1102 |  |  |  |  |  |  | } else { | 
| 1103 |  |  |  |  |  |  | # ...or just from start of AppInfo block | 
| 1104 | 0 |  |  |  |  | 0 | $sort_offset = $appinfo_offset + | 
| 1105 |  |  |  |  |  |  | length($appinfo_block); | 
| 1106 |  |  |  |  |  |  | } | 
| 1107 |  |  |  |  |  |  | } | 
| 1108 |  |  |  |  |  |  |  | 
| 1109 |  |  |  |  |  |  | # Write header | 
| 1110 |  |  |  |  |  |  | $header = pack "a32 n n N N N N N N a4 a4 N", | 
| 1111 |  |  |  |  |  |  | $self->{name}, | 
| 1112 |  |  |  |  |  |  | $attributes, | 
| 1113 |  |  |  |  |  |  | $self->{version}, | 
| 1114 |  |  |  |  |  |  | $self->{ctime} + $EPOCH_1904, | 
| 1115 |  |  |  |  |  |  | $self->{mtime} + $EPOCH_1904, | 
| 1116 |  |  |  |  |  |  | $self->{baktime} + $EPOCH_1904, | 
| 1117 |  |  |  |  |  |  | $self->{modnum}, | 
| 1118 |  |  |  |  |  |  | $appinfo_offset, | 
| 1119 |  |  |  |  |  |  | $sort_offset, | 
| 1120 |  |  |  |  |  |  | $self->{type}, | 
| 1121 |  |  |  |  |  |  | $self->{creator}, | 
| 1122 | 3 |  |  |  |  | 20 | $self->{uniqueIDseed}; | 
| 1123 |  |  |  |  |  |  | ; | 
| 1124 |  |  |  |  |  |  |  | 
| 1125 | 3 |  |  |  |  | 23 | print $OFILE "$header"; | 
| 1126 |  |  |  |  |  |  |  | 
| 1127 |  |  |  |  |  |  | # Write index header | 
| 1128 | 3 |  |  |  |  | 5 | my $index_header; | 
| 1129 |  |  |  |  |  |  |  | 
| 1130 | 3 |  |  |  |  | 8 | $index_header = pack "N n", 0, ($#record_data+1); | 
| 1131 | 3 |  |  |  |  | 6 | print $OFILE "$index_header"; | 
| 1132 |  |  |  |  |  |  |  | 
| 1133 |  |  |  |  |  |  | # Write index | 
| 1134 | 3 |  |  |  |  | 3 | my $rec_offset;		# Offset of next record/resource | 
| 1135 |  |  |  |  |  |  |  | 
| 1136 |  |  |  |  |  |  | # Calculate offset of first record/resource | 
| 1137 | 3 | 50 |  |  |  | 11 | if ($sort_offset != 0) | 
|  |  | 50 |  |  |  |  |  | 
| 1138 |  |  |  |  |  |  | { | 
| 1139 | 0 |  |  |  |  | 0 | $rec_offset = $sort_offset + length($sort_block); | 
| 1140 |  |  |  |  |  |  | } elsif ($appinfo_offset != 0) | 
| 1141 |  |  |  |  |  |  | { | 
| 1142 | 0 |  |  |  |  | 0 | $rec_offset = $appinfo_offset + length($appinfo_block); | 
| 1143 |  |  |  |  |  |  | } else { | 
| 1144 | 3 |  |  |  |  | 4 | $rec_offset = $HeaderLen + $index_len + 2; | 
| 1145 |  |  |  |  |  |  | } | 
| 1146 |  |  |  |  |  |  |  | 
| 1147 | 3 | 50 | 33 |  |  | 17 | if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) | 
| 1148 |  |  |  |  |  |  | { | 
| 1149 |  |  |  |  |  |  | # Resource database | 
| 1150 |  |  |  |  |  |  | # Record database | 
| 1151 | 0 |  |  |  |  | 0 | my $rsrc_data; | 
| 1152 |  |  |  |  |  |  |  | 
| 1153 | 0 |  |  |  |  | 0 | foreach $rsrc_data (@record_data) | 
| 1154 |  |  |  |  |  |  | { | 
| 1155 | 0 |  |  |  |  | 0 | my $type; | 
| 1156 |  |  |  |  |  |  | my $id; | 
| 1157 | 0 |  |  |  |  | 0 | my $data; | 
| 1158 | 0 |  |  |  |  | 0 | my $index_data; | 
| 1159 |  |  |  |  |  |  |  | 
| 1160 | 0 |  |  |  |  | 0 | ($type, $id, $data) = @{$rsrc_data}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1161 | 0 |  |  |  |  | 0 | $index_data = pack "a4 n N", | 
| 1162 |  |  |  |  |  |  | $type, | 
| 1163 |  |  |  |  |  |  | $id, | 
| 1164 |  |  |  |  |  |  | $rec_offset; | 
| 1165 | 0 |  |  |  |  | 0 | print $OFILE "$index_data"; | 
| 1166 |  |  |  |  |  |  |  | 
| 1167 | 0 |  |  |  |  | 0 | $rec_offset += length($data); | 
| 1168 |  |  |  |  |  |  | } | 
| 1169 |  |  |  |  |  |  | } else { | 
| 1170 |  |  |  |  |  |  | # Record database | 
| 1171 | 3 |  |  |  |  | 9 | my $rec_data; | 
| 1172 |  |  |  |  |  |  |  | 
| 1173 | 3 |  |  |  |  | 5 | foreach $rec_data (@record_data) | 
| 1174 |  |  |  |  |  |  | { | 
| 1175 | 6 |  |  |  |  | 7 | my $attributes; | 
| 1176 |  |  |  |  |  |  | my $data; | 
| 1177 | 0 |  |  |  |  | 0 | my $id; | 
| 1178 | 0 |  |  |  |  | 0 | my $index_data; | 
| 1179 |  |  |  |  |  |  |  | 
| 1180 |  |  |  |  |  |  | # XXX - Probably shouldn't write this record if | 
| 1181 |  |  |  |  |  |  | # length($data) == 0 | 
| 1182 | 6 |  |  |  |  | 8 | ($attributes, $id, $data) = @{$rec_data}; | 
|  | 6 |  |  |  |  | 17 |  | 
| 1183 |  |  |  |  |  |  |  | 
| 1184 | 6 | 50 |  |  |  | 16 | if (length($data) == 0) | 
| 1185 |  |  |  |  |  |  | { | 
| 1186 | 0 |  |  |  |  | 0 | warn printf("Write: Warning: record 0x%08x has length 0\n", $id) | 
| 1187 |  |  |  |  |  |  | } | 
| 1188 |  |  |  |  |  |  |  | 
| 1189 | 6 |  |  |  |  | 17 | $index_data = pack "N C C3", | 
| 1190 |  |  |  |  |  |  | $rec_offset, | 
| 1191 |  |  |  |  |  |  | $attributes, | 
| 1192 |  |  |  |  |  |  | ($id >> 16) & 0xff, | 
| 1193 |  |  |  |  |  |  | ($id >> 8) & 0xff, | 
| 1194 |  |  |  |  |  |  | $id & 0xff; | 
| 1195 | 6 |  |  |  |  | 10 | print $OFILE "$index_data"; | 
| 1196 |  |  |  |  |  |  |  | 
| 1197 | 6 |  |  |  |  | 11 | $rec_offset += length($data); | 
| 1198 |  |  |  |  |  |  | } | 
| 1199 |  |  |  |  |  |  | } | 
| 1200 |  |  |  |  |  |  |  | 
| 1201 |  |  |  |  |  |  | # Write the two NULs | 
| 1202 | 3 | 50 |  |  |  | 10 | if (length($self->{"2NULs"}) == 2) | 
| 1203 |  |  |  |  |  |  | { | 
| 1204 | 3 |  |  |  |  | 6 | print $OFILE $self->{"2NULs"}; | 
| 1205 |  |  |  |  |  |  | } else { | 
| 1206 | 0 |  |  |  |  | 0 | print $OFILE "\0\0"; | 
| 1207 |  |  |  |  |  |  | } | 
| 1208 |  |  |  |  |  |  |  | 
| 1209 |  |  |  |  |  |  | # Write AppInfo block | 
| 1210 | 3 | 50 |  |  |  | 7 | print $OFILE $appinfo_block unless $appinfo_offset == 0; | 
| 1211 |  |  |  |  |  |  |  | 
| 1212 |  |  |  |  |  |  | # Write sort block | 
| 1213 | 3 | 50 |  |  |  | 8 | print $OFILE $sort_block unless $sort_offset == 0; | 
| 1214 |  |  |  |  |  |  |  | 
| 1215 |  |  |  |  |  |  | # Write record/resource list | 
| 1216 | 3 |  |  |  |  | 3 | my $record; | 
| 1217 | 3 |  |  |  |  | 8 | foreach $record (@record_data) | 
| 1218 |  |  |  |  |  |  | { | 
| 1219 | 6 |  |  |  |  | 5 | my $data; | 
| 1220 |  |  |  |  |  |  |  | 
| 1221 | 6 | 50 | 33 |  |  | 30 | if ($self->{attributes}{resource} || $self->{'attributes'}{'ResDB'}) | 
| 1222 |  |  |  |  |  |  | { | 
| 1223 |  |  |  |  |  |  | # Resource database | 
| 1224 | 0 |  |  |  |  | 0 | my $type; | 
| 1225 |  |  |  |  |  |  | my $id; | 
| 1226 |  |  |  |  |  |  |  | 
| 1227 | 0 |  |  |  |  | 0 | ($type, $id, $data) = @{$record}; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1228 |  |  |  |  |  |  | } else { | 
| 1229 | 6 |  |  |  |  | 6 | my $attributes; | 
| 1230 |  |  |  |  |  |  | my $id; | 
| 1231 |  |  |  |  |  |  |  | 
| 1232 | 6 |  |  |  |  | 7 | ($attributes, $id, $data) = @{$record}; | 
|  | 6 |  |  |  |  | 17 |  | 
| 1233 |  |  |  |  |  |  | } | 
| 1234 | 6 |  |  |  |  | 16 | print $OFILE $data; | 
| 1235 |  |  |  |  |  |  | } | 
| 1236 |  |  |  |  |  |  |  | 
| 1237 | 3 |  |  |  |  | 290 | close $OFILE; | 
| 1238 |  |  |  |  |  |  | } | 
| 1239 |  |  |  |  |  |  |  | 
| 1240 |  |  |  |  |  |  | =head2 new_Record | 
| 1241 |  |  |  |  |  |  |  | 
| 1242 |  |  |  |  |  |  | $record = Palm::PDB->new_Record(); | 
| 1243 |  |  |  |  |  |  | $record = new_Record Palm::PDB; | 
| 1244 |  |  |  |  |  |  |  | 
| 1245 |  |  |  |  |  |  | Creates a new record, with the bare minimum needed: | 
| 1246 |  |  |  |  |  |  |  | 
| 1247 |  |  |  |  |  |  | $record->{'category'} | 
| 1248 |  |  |  |  |  |  | $record->{'attributes'}{'Dirty'} | 
| 1249 |  |  |  |  |  |  | $record->{'id'} | 
| 1250 |  |  |  |  |  |  |  | 
| 1251 |  |  |  |  |  |  | The ``Dirty'' attribute is originally set, since this function will | 
| 1252 |  |  |  |  |  |  | usually be called to create records to be added to a database. | 
| 1253 |  |  |  |  |  |  |  | 
| 1254 |  |  |  |  |  |  | C does B add the new record to a PDB. For that, | 
| 1255 |  |  |  |  |  |  | you want C. | 
| 1256 |  |  |  |  |  |  |  | 
| 1257 |  |  |  |  |  |  | =cut | 
| 1258 |  |  |  |  |  |  |  | 
| 1259 |  |  |  |  |  |  | # PDB::new_Record() | 
| 1260 |  |  |  |  |  |  | # Create a new, initialized record, and return a reference to it. | 
| 1261 |  |  |  |  |  |  | # The record is initially marked as being dirty, since that's usually | 
| 1262 |  |  |  |  |  |  | # the Right Thing. | 
| 1263 |  |  |  |  |  |  | sub new_Record | 
| 1264 |  |  |  |  |  |  | { | 
| 1265 | 6 |  |  | 6 | 1 | 10 | my $classname = shift; | 
| 1266 | 6 |  |  |  |  | 8 | my $retval = {}; | 
| 1267 |  |  |  |  |  |  |  | 
| 1268 |  |  |  |  |  |  | # Initialize the record | 
| 1269 | 6 |  |  |  |  | 14 | $retval->{'category'} = 0;	# Unfiled, by convention | 
| 1270 | 6 |  |  |  |  | 18 | $retval->{'attributes'} = { | 
| 1271 |  |  |  |  |  |  | #		expunged	=> 0, | 
| 1272 |  |  |  |  |  |  | dirty		=> 1,	# Note: originally dirty | 
| 1273 |  |  |  |  |  |  | 'Dirty'		=> 1, | 
| 1274 |  |  |  |  |  |  | #		deleted		=> 0, | 
| 1275 |  |  |  |  |  |  | #		private		=> 0, | 
| 1276 |  |  |  |  |  |  | #		archive         => 0, | 
| 1277 |  |  |  |  |  |  | }; | 
| 1278 | 6 |  |  |  |  | 8 | $retval->{'id'} = 0;		# Initially, no record ID | 
| 1279 |  |  |  |  |  |  |  | 
| 1280 | 6 |  |  |  |  | 12 | return $retval; | 
| 1281 |  |  |  |  |  |  | } | 
| 1282 |  |  |  |  |  |  |  | 
| 1283 |  |  |  |  |  |  | =head2 append_Record | 
| 1284 |  |  |  |  |  |  |  | 
| 1285 |  |  |  |  |  |  | $record  = $pdb->append_Record; | 
| 1286 |  |  |  |  |  |  | $record2 = $pdb->append_Record($record1); | 
| 1287 |  |  |  |  |  |  |  | 
| 1288 |  |  |  |  |  |  | If called without any arguments, creates a new record with | 
| 1289 |  |  |  |  |  |  | L, and appends it to $pdb. | 
| 1290 |  |  |  |  |  |  |  | 
| 1291 |  |  |  |  |  |  | If given a reference to a record, appends that record to | 
| 1292 |  |  |  |  |  |  | @{$pdb->{records}}. | 
| 1293 |  |  |  |  |  |  |  | 
| 1294 |  |  |  |  |  |  | Returns a reference to the newly-appended record. | 
| 1295 |  |  |  |  |  |  |  | 
| 1296 |  |  |  |  |  |  | This method updates $pdb's "last modification" time. | 
| 1297 |  |  |  |  |  |  |  | 
| 1298 |  |  |  |  |  |  | =cut | 
| 1299 |  |  |  |  |  |  | #' | 
| 1300 |  |  |  |  |  |  |  | 
| 1301 |  |  |  |  |  |  | # append_Record | 
| 1302 |  |  |  |  |  |  | # Append the given records to the database's list of records. If no | 
| 1303 |  |  |  |  |  |  | # records are given, create one, append it, and return a reference to | 
| 1304 |  |  |  |  |  |  | # it. | 
| 1305 |  |  |  |  |  |  | sub append_Record | 
| 1306 |  |  |  |  |  |  | { | 
| 1307 | 6 |  |  | 6 | 1 | 10 | my $self = shift; | 
| 1308 |  |  |  |  |  |  |  | 
| 1309 | 6 | 50 |  |  |  | 18 | if ($#_ < 0) | 
| 1310 |  |  |  |  |  |  | { | 
| 1311 |  |  |  |  |  |  | # No arguments given. Create a new record. | 
| 1312 | 6 |  |  |  |  | 26 | my $record = $self->new_Record; | 
| 1313 |  |  |  |  |  |  |  | 
| 1314 | 6 |  |  |  |  | 9 | push @{$self->{records}}, $record; | 
|  | 6 |  |  |  |  | 14 |  | 
| 1315 |  |  |  |  |  |  |  | 
| 1316 |  |  |  |  |  |  | # Update the "last modification time". | 
| 1317 | 6 |  |  |  |  | 11 | $self->{mtime} = time; | 
| 1318 |  |  |  |  |  |  |  | 
| 1319 | 6 |  |  |  |  | 14 | return $record; | 
| 1320 |  |  |  |  |  |  | } | 
| 1321 |  |  |  |  |  |  |  | 
| 1322 |  |  |  |  |  |  | # At least one argument was given. Append all of the arguments | 
| 1323 |  |  |  |  |  |  | # to the list of records, and return the first one. | 
| 1324 | 0 |  |  |  |  |  | push @{$self->{records}}, @_; | 
|  | 0 |  |  |  |  |  |  | 
| 1325 |  |  |  |  |  |  |  | 
| 1326 |  |  |  |  |  |  | # Update the "last modification time". | 
| 1327 | 0 |  |  |  |  |  | $self->{mtime} = time; | 
| 1328 |  |  |  |  |  |  |  | 
| 1329 | 0 |  |  |  |  |  | return $_[0]; | 
| 1330 |  |  |  |  |  |  | } | 
| 1331 |  |  |  |  |  |  |  | 
| 1332 |  |  |  |  |  |  | =head2 | 
| 1333 |  |  |  |  |  |  |  | 
| 1334 |  |  |  |  |  |  | $resource = Palm::PDB->new_Resource(); | 
| 1335 |  |  |  |  |  |  | $resource = new_Resource Palm::PDB; | 
| 1336 |  |  |  |  |  |  |  | 
| 1337 |  |  |  |  |  |  | Creates a new resource and initializes | 
| 1338 |  |  |  |  |  |  |  | 
| 1339 |  |  |  |  |  |  | $resource->{type} | 
| 1340 |  |  |  |  |  |  | $resource->{id} | 
| 1341 |  |  |  |  |  |  |  | 
| 1342 |  |  |  |  |  |  | =cut | 
| 1343 |  |  |  |  |  |  |  | 
| 1344 |  |  |  |  |  |  | # new_Resource | 
| 1345 |  |  |  |  |  |  | # Create a new, initialized resource, and return a reference to it. | 
| 1346 |  |  |  |  |  |  | sub new_Resource | 
| 1347 |  |  |  |  |  |  | { | 
| 1348 | 0 |  |  | 0 | 0 |  | my $classname = shift; | 
| 1349 | 0 |  |  |  |  |  | my $retval = {}; | 
| 1350 |  |  |  |  |  |  |  | 
| 1351 |  |  |  |  |  |  | # Initialize the resource | 
| 1352 | 0 |  |  |  |  |  | $retval->{type} = "\0\0\0\0"; | 
| 1353 | 0 |  |  |  |  |  | $retval->{id} = 0; | 
| 1354 |  |  |  |  |  |  |  | 
| 1355 | 0 |  |  |  |  |  | return $retval; | 
| 1356 |  |  |  |  |  |  | } | 
| 1357 |  |  |  |  |  |  |  | 
| 1358 |  |  |  |  |  |  | =head2 append_Resource | 
| 1359 |  |  |  |  |  |  |  | 
| 1360 |  |  |  |  |  |  | $resource  = $pdb->append_Resource; | 
| 1361 |  |  |  |  |  |  | $resource2 = $pdb->append_Resource($resource1); | 
| 1362 |  |  |  |  |  |  |  | 
| 1363 |  |  |  |  |  |  | If called without any arguments, creates a new resource with | 
| 1364 |  |  |  |  |  |  | L, and appends it to $pdb. | 
| 1365 |  |  |  |  |  |  |  | 
| 1366 |  |  |  |  |  |  | If given a reference to a resource, appends that resource to | 
| 1367 |  |  |  |  |  |  | @{$pdb->{resources}}. | 
| 1368 |  |  |  |  |  |  |  | 
| 1369 |  |  |  |  |  |  | Returns a reference to the newly-appended resource. | 
| 1370 |  |  |  |  |  |  |  | 
| 1371 |  |  |  |  |  |  | This method updates $pdb's "last modification" time. | 
| 1372 |  |  |  |  |  |  |  | 
| 1373 |  |  |  |  |  |  | =cut | 
| 1374 |  |  |  |  |  |  | #' | 
| 1375 |  |  |  |  |  |  |  | 
| 1376 |  |  |  |  |  |  | # append_Resource | 
| 1377 |  |  |  |  |  |  | # Append the given resources to the database's list of resources. If no | 
| 1378 |  |  |  |  |  |  | # resources are given, create one, append it, and return a reference to | 
| 1379 |  |  |  |  |  |  | # it. | 
| 1380 |  |  |  |  |  |  | sub append_Resource | 
| 1381 |  |  |  |  |  |  | { | 
| 1382 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 1383 |  |  |  |  |  |  |  | 
| 1384 | 0 | 0 |  |  |  |  | if ($#_ < 0) | 
| 1385 |  |  |  |  |  |  | { | 
| 1386 |  |  |  |  |  |  | # No arguments given. Create a new resource | 
| 1387 | 0 |  |  |  |  |  | my $resource = $self->new_Resource; | 
| 1388 |  |  |  |  |  |  |  | 
| 1389 | 0 |  |  |  |  |  | push @{$self->{resources}}, $resource; | 
|  | 0 |  |  |  |  |  |  | 
| 1390 |  |  |  |  |  |  |  | 
| 1391 |  |  |  |  |  |  | # Update the "last modification time". | 
| 1392 | 0 |  |  |  |  |  | $self->{mtime} = time; | 
| 1393 |  |  |  |  |  |  |  | 
| 1394 | 0 |  |  |  |  |  | return $resource; | 
| 1395 |  |  |  |  |  |  | } | 
| 1396 |  |  |  |  |  |  |  | 
| 1397 |  |  |  |  |  |  | # At least one argument was given. Append all of the arguments | 
| 1398 |  |  |  |  |  |  | # to the list of resources, and return the first one. | 
| 1399 | 0 |  |  |  |  |  | push @{$self->{resources}}, @_; | 
|  | 0 |  |  |  |  |  |  | 
| 1400 |  |  |  |  |  |  |  | 
| 1401 |  |  |  |  |  |  | # Update the "last modification time". | 
| 1402 | 0 |  |  |  |  |  | $self->{mtime} = time; | 
| 1403 |  |  |  |  |  |  |  | 
| 1404 | 0 |  |  |  |  |  | return $_[0]; | 
| 1405 |  |  |  |  |  |  | } | 
| 1406 |  |  |  |  |  |  |  | 
| 1407 |  |  |  |  |  |  | =head2 findRecordByID | 
| 1408 |  |  |  |  |  |  |  | 
| 1409 |  |  |  |  |  |  | $record = $pdb->findRecordByID($id); | 
| 1410 |  |  |  |  |  |  |  | 
| 1411 |  |  |  |  |  |  | Looks through the list of records in $pdb, and returns a reference to | 
| 1412 |  |  |  |  |  |  | the record with ID $id, or the undefined value if no such record was | 
| 1413 |  |  |  |  |  |  | found. | 
| 1414 |  |  |  |  |  |  |  | 
| 1415 |  |  |  |  |  |  | =cut | 
| 1416 |  |  |  |  |  |  |  | 
| 1417 |  |  |  |  |  |  | # findRecordByID | 
| 1418 |  |  |  |  |  |  | # Returns a reference to the record with the given ID, or 'undef' if | 
| 1419 |  |  |  |  |  |  | # it doesn't exist. | 
| 1420 |  |  |  |  |  |  | sub findRecordByID | 
| 1421 |  |  |  |  |  |  | { | 
| 1422 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 1423 | 0 |  |  |  |  |  | my $id = shift; | 
| 1424 |  |  |  |  |  |  |  | 
| 1425 | 0 | 0 |  |  |  |  | return undef if $id eq ""; | 
| 1426 |  |  |  |  |  |  |  | 
| 1427 | 0 |  |  |  |  |  | for (@{$self->{records}}) | 
|  | 0 |  |  |  |  |  |  | 
| 1428 |  |  |  |  |  |  | { | 
| 1429 | 0 | 0 |  |  |  |  | next unless $_->{id} == $id; | 
| 1430 | 0 |  |  |  |  |  | return $_;		# Found it | 
| 1431 |  |  |  |  |  |  | } | 
| 1432 |  |  |  |  |  |  |  | 
| 1433 | 0 |  |  |  |  |  | return undef;			# Not found | 
| 1434 |  |  |  |  |  |  | } | 
| 1435 |  |  |  |  |  |  |  | 
| 1436 |  |  |  |  |  |  | =head2 delete_Record | 
| 1437 |  |  |  |  |  |  |  | 
| 1438 |  |  |  |  |  |  | $pdb->delete_Record($record, $expunge); | 
| 1439 |  |  |  |  |  |  |  | 
| 1440 |  |  |  |  |  |  | Marks $record for deletion, so that it will be deleted from the | 
| 1441 |  |  |  |  |  |  | database at the next sync. | 
| 1442 |  |  |  |  |  |  |  | 
| 1443 |  |  |  |  |  |  | If $expunge is false or omitted, the record will be marked | 
| 1444 |  |  |  |  |  |  | for deletion with archival. If $expunge is true, the record will be | 
| 1445 |  |  |  |  |  |  | marked for deletion without archival. | 
| 1446 |  |  |  |  |  |  |  | 
| 1447 |  |  |  |  |  |  | This method updates $pdb's "last modification" time. | 
| 1448 |  |  |  |  |  |  |  | 
| 1449 |  |  |  |  |  |  | =cut | 
| 1450 |  |  |  |  |  |  | #' | 
| 1451 |  |  |  |  |  |  |  | 
| 1452 |  |  |  |  |  |  | # delete_Record | 
| 1453 |  |  |  |  |  |  | # $pdb->delete_Record($record ?, $expunge?) | 
| 1454 |  |  |  |  |  |  | # | 
| 1455 |  |  |  |  |  |  | # Mark the given record for deletion. If $expunge is true, mark the | 
| 1456 |  |  |  |  |  |  | # record for deletion without an archive. | 
| 1457 |  |  |  |  |  |  |  | 
| 1458 |  |  |  |  |  |  | sub delete_Record | 
| 1459 |  |  |  |  |  |  | { | 
| 1460 | 0 |  |  | 0 | 1 |  | my $self = shift; | 
| 1461 | 0 |  |  |  |  |  | my $record = shift; | 
| 1462 | 0 |  |  |  |  |  | my $expunge = shift; | 
| 1463 |  |  |  |  |  |  |  | 
| 1464 | 0 |  |  |  |  |  | $record->{attributes}{deleted} = 1; | 
| 1465 | 0 | 0 |  |  |  |  | if ($expunge) | 
| 1466 |  |  |  |  |  |  | { | 
| 1467 | 0 |  |  |  |  |  | $record->{attributes}{expunged} = 1; | 
| 1468 | 0 |  |  |  |  |  | $record->{attributes}{archive} = 0; | 
| 1469 |  |  |  |  |  |  | } else { | 
| 1470 | 0 |  |  |  |  |  | $record->{attributes}{expunged} = 0; | 
| 1471 | 0 |  |  |  |  |  | $record->{attributes}{archive} = 1; | 
| 1472 |  |  |  |  |  |  | } | 
| 1473 |  |  |  |  |  |  |  | 
| 1474 |  |  |  |  |  |  | # Update the "last modification time". | 
| 1475 | 0 |  |  |  |  |  | $self->{mtime} = time; | 
| 1476 |  |  |  |  |  |  | } | 
| 1477 |  |  |  |  |  |  |  | 
| 1478 |  |  |  |  |  |  | 1; | 
| 1479 |  |  |  |  |  |  |  | 
| 1480 |  |  |  |  |  |  | __END__ |