Annotation of palm/Palm-Keyring/examples/KeyRingpdbdump, Revision 1.2
1.1 andrew 1: #!/usr/bin/perl
2: #
3: # Dump a Palm PDB or PRC database.
4: #
1.2 ! andrew 5: # $Id: KeyRingpdbdump,v 1.1 2006/01/26 20:54:19 andrew Exp $
! 6: # $RedRiver: KeyRingpdbdump,v 1.1 2006/01/26 20:54:19 andrew Exp $
1.1 andrew 7:
8: use strict;
9: use Palm::PDB;
10: use Palm::Raw;
11:
12: use lib 'lib';
13: # Load handlers for the built-in apps by default
14: use Palm::Memo;
15: use Palm::Address;
16: use Palm::Datebook;
17: use Palm::Mail;
18: use Palm::ToDo;
19: use Palm::Keyring;
20:
21: use vars qw( $VERSION %PDBHandlers %PRCHandlers $hexdump );
22:
23: $VERSION = sprintf "%d.%03d_%03d_%03d",
1.2 ! andrew 24: '$Revision: 1.1 $ ' =~ m{(\d+)(?:\.(\d+))};
1.1 andrew 25:
26: *PDBHandlers = *Palm::PDB::PDBHandlers;
27: *PRCHandlers = *Palm::PDB::PRCHandlers;
28:
29: &Palm::PDB::RegisterPRCHandlers("Palm::Raw",
30: [ "", "" ]
31: );
32:
33: $hexdump = 1; # By default, print hex dumps of everything
34:
35: # Parse command-line arguments
36: my $arg;
37: while (($arg = $ARGV[0]) =~ /^-/)
38: {
39: $arg = shift;
40:
41: if (($arg eq "-h") || ($arg eq "-help") || ($arg eq "--help"))
42: {
43: print <<EOT;
44: Usage: $0 [options] pdb-file
45: Options:
46: -h, -help, --help This message.
47: -nohex Don't print hex dumps
48: -M<module> Load <module> (e.g., -MPalm::Address)
49: EOT
50: #'
51: exit 0;
52: } elsif ($arg =~ /^-M/)
53: {
54: eval "use $';";
55: } elsif ($arg eq "-nohex")
56: {
57: $hexdump = 0;
58: } else {
59: die "Unrecognized option: $arg\n";
60: }
61: }
62:
63: my $fname = shift;
64:
65: die "No such file: $fname\n" if ! -f $fname;
66:
67: my $EPOCH_1904 = 2082844800; # Difference between Palm's
68: # epoch (Jan. 1, 1904) and
69: # Unix's epoch (Jan. 1, 1970),
70: # in seconds.
71: my $HeaderLen = 32+2+2+(9*4); # Size of database header
72: my $RecIndexHeaderLen = 6; # Size of record index header
73: my $IndexRecLen = 8; # Length of record index entry
74: my $IndexRsrcLen = 10; # Length of resource index entry
75:
76: #%PDBHandlers = (); # Record handler map
77: #%PRCHandlers = (); # Resource handler map
78:
79:
80: #&Palm::PDB::rawread($fname);
81: &rawread($fname);
82:
83: #package Palm::PDB; # XXX - Gross hack!
84: sub rawread
85: {
86: my $self = new Palm::Raw;
87: my $fname = shift; # Filename to read from
88: my $buf; # Buffer into which to read stuff
89:
90: # Open database file
91: open PDB, "< $fname" or die "Can't open \"$fname\": $!\n";
92: binmode PDB; # Parse as binary file under MS-DOS
93:
94: # Get the size of the file. It'll be useful later
95: seek PDB, 0, 2; # 2 == SEEK_END. Seek to the end.
96: $self->{_size} = tell PDB;
97: print "File size: $self->{_size}\n";
98: seek PDB, 0, 0; # 0 == SEEK_START. Rewind to the beginning.
99:
100: # Read header
101: my $name;
102: my $attributes;
103: my $version;
104: my $ctime;
105: my $mtime;
106: my $baktime;
107: my $modnum;
108: my $appinfo_offset;
109: my $sort_offset;
110: my $type;
111: my $creator;
112: my $uniqueIDseed;
113:
114: read PDB, $buf, $HeaderLen; # Read the PDB header
115: print "Database header:\n";
116: if ($hexdump)
117: {
118: &hexdump(" ", $buf);
119: print "\n";
120: }
121:
122: # Split header into its component fields
123: ($name, $attributes, $version, $ctime, $mtime, $baktime,
124: $modnum, $appinfo_offset, $sort_offset, $type, $creator,
125: $uniqueIDseed) =
126: unpack "a32 n n N N N N N N a4 a4 N", $buf;
127:
128: ($self->{name} = $name) =~ s/\0*$//;
129: $self->{attributes}{resource} = 1 if $attributes & 0x0001;
130: $self->{attributes}{"read-only"} = 1 if $attributes & 0x0002;
131: $self->{attributes}{"AppInfo dirty"} = 1 if $attributes & 0x0004;
132: $self->{attributes}{backup} = 1 if $attributes & 0x0008;
133: $self->{attributes}{"OK newer"} = 1 if $attributes & 0x0010;
134: $self->{attributes}{reset} = 1 if $attributes & 0x0020;
135: $self->{attributes}{open} = 1 if $attributes & 0x0040;
136: $self->{attributes}{launchable} = 1 if $attributes & 0x0200;
137: $self->{version} = $version;
138: $self->{ctime} = $ctime - $EPOCH_1904;
139: $self->{mtime} = $mtime - $EPOCH_1904;
140: $self->{baktime} = $baktime - $EPOCH_1904;
141: $self->{modnum} = $modnum;
142: # _appinfo_offset and _sort_offset are private fields
143: $self->{_appinfo_offset} = $appinfo_offset;
144: $self->{_sort_offset} = $sort_offset;
145: $self->{type} = $type;
146: $self->{creator} = $creator;
147: $self->{uniqueIDseed} = $uniqueIDseed;
148:
149: print <<EOT;
150: Name: $name
151: Attributes: @{[sprintf("0x%02x", $attributes)]}
152: EOT
153: print " "x19;
154: print " LAUNCHABLE" if $self->{attributes}{launchable};
155: print " OPEN" if $self->{attributes}{open};
156: print " RESET" if $self->{attributes}{reset};
157: print " OKNEWER" if $self->{attributes}{"OK newer"};
158: print " BACKUP" if $self->{attributes}{backup};
159: print " APPINFO-DIRTY" if $self->{attributes}{"AppInfo dirty"};
160: print " READ-ONLY" if $self->{attributes}{"read-only"};
161: print " RESOURCE" if $self->{attributes}{resource};
162: print "\n";
163: print <<EOT;
164: Version: $version
165: Ctime: $ctime @{[scalar(localtime($ctime-$EPOCH_1904))]}
166: Mtime: $mtime @{[scalar(localtime($mtime-$EPOCH_1904))]}
167: Backup time: $baktime @{[scalar(localtime($baktime-$EPOCH_1904))]}
168: Mod number: $modnum
169: AppInfo offset: $appinfo_offset
170: Sort offset: $sort_offset
171: Type: $type
172: Creator: $creator
173: Unique ID seed: $uniqueIDseed
174:
175: EOT
176:
177: # Rebless this PDB object, depending on its type and/or
178: # creator. This allows us to magically invoke the proper
179: # &Parse*() function on the various parts of the database.
180:
181: # Look for most specific handlers first, least specific ones
182: # last. That is, first look for a handler that deals
183: # specifically with this database's creator and type, then for
184: # one that deals with this database's creator and any type,
185: # and finally for one that deals with anything.
186:
187: my $handler;
188: if ($self->{attributes}{resource})
189: {
190: # Look among resource handlers
191: $handler = $PRCHandlers{$self->{creator}}{$self->{type}} ||
192: $PRCHandlers{undef}{$self->{type}} ||
193: $PRCHandlers{$self->{creator}}{""} ||
194: $PRCHandlers{""}{""};
195: } else {
196: # Look among record handlers
197: $handler = $PDBHandlers{$self->{creator}}{$self->{type}} ||
198: $PDBHandlers{""}{$self->{type}} ||
199: $PDBHandlers{$self->{creator}}{""} ||
200: $PDBHandlers{""}{""};
201: }
202:
203: if (defined($handler))
204: {
205: bless $self, $handler;
206: } else {
207: # XXX - This should probably return 'undef' or something,
208: # rather than die.
209: die "No handler defined for creator \"$creator\", type \"$type\"\n";
210: }
211:
212: ## Read record/resource index
213: # Read index header
214: read PDB, $buf, $RecIndexHeaderLen;
215: print "Record/resource index header:\n";
216: if ($hexdump)
217: {
218: &hexdump(" ", $buf);
219: print "\n";
220: }
221:
222: my $next_index;
223: my $numrecs;
224:
225: ($next_index, $numrecs) = unpack "N n", $buf;
226: $self->{_numrecs} = $numrecs;
227:
228: print <<EOT;
229: Next index: $next_index
230: # records: $numrecs
231:
232: EOT
233:
234: # Read the index itself
235: if ($self->{attributes}{resource})
236: {
237: &_load_rsrc_index($self, \*PDB);
238: } else {
239: &_load_rec_index($self, \*PDB);
240: }
241:
242: # Ignore the two NUL bytes that are usually here. We'll seek()
243: # around them later.
244:
245: # Read AppInfo block, if it exists
246: if ($self->{_appinfo_offset} != 0)
247: {
248: &_load_appinfo_block($self, \*PDB);
249: }
250:
251: # Read sort block, if it exists
252: if ($self->{_sort_offset} != 0)
253: {
254: &_load_sort_block($self, \*PDB);
255: }
256:
257: # Read record/resource list
258: if ($self->{attributes}{resource})
259: {
260: &_load_resources($self, \*PDB);
261: } else {
262: &_load_records($self, \*PDB);
263: }
264:
265: # These keys were needed for parsing the file, but are not
266: # needed any longer. Delete them.
267: delete $self->{_index};
268: delete $self->{_numrecs};
269: delete $self->{_appinfo_offset};
270: delete $self->{_sort_offset};
271: delete $self->{_size};
272:
273: close PDB;
274: }
275:
276:
277: # _load_rec_index
278: # Private function. Read the record index, for a record database
279: sub _load_rec_index
280: {
281: my $pdb = shift;
282: my $fh = shift; # Input file handle
283: my $i;
284: my $lastoffset = 0;
285:
286: print "Record index:\n";
287:
288: # Read each record index entry in turn
289: for ($i = 0; $i < $pdb->{_numrecs}; $i++)
290: {
291: my $buf; # Input buffer
292:
293: print " Record index entry $i\n";
294:
295: # Read the next record index entry
296: my $offset;
297: my $attributes;
298: my @id; # Raw ID
299: my $id; # Numerical ID
300: my $entry = {}; # Parsed index entry
301:
302: read $fh, $buf, $IndexRecLen;
303: if ($hexdump)
304: {
305: &hexdump(" ", $buf);
306: print "\n";
307: }
308:
309: # The ID field is a bit weird: it's represented as 3
310: # bytes, but it's really a double word (long) value.
311:
312: ($offset, $attributes, @id) = unpack "N C C3", $buf;
313: if ($offset == $lastoffset)
314: {
315: print STDERR "Record $i has same offset as previous one: $offset\n";
316: }
317: $lastoffset = $offset;
318:
319: $entry->{offset} = $offset;
320: $entry->{attributes}{expunged} = 1 if $attributes & 0x80;
321: $entry->{attributes}{dirty} = 1 if $attributes & 0x40;
322: $entry->{attributes}{deleted} = 1 if $attributes & 0x20;
323: $entry->{attributes}{private} = 1 if $attributes & 0x10;
324: $entry->{id} = ($id[0] << 16) |
325: ($id[1] << 8) |
326: $id[2];
327:
328: # The lower 4 bits of the attributes field are
329: # overloaded: If the record has been deleted and/or
330: # expunged, then bit 0x08 indicates whether the record
331: # should be archived. Otherwise (if it's an ordinary,
332: # non-deleted record), the lower 4 bits specify the
333: # category that the record belongs in.
334: if (($attributes & 0xa0) == 0)
335: {
336: $entry->{category} = $attributes & 0x0f;
337: } else {
338: $entry->{attributes}{archive} = 1
339: if $attributes & 0x08;
340: }
341:
342: print <<EOT;
343: Offset: $offset
344: Attributes: @{[sprintf("0x%02x", $attributes), keys %{$entry->{attributes}}]}
345: Category: $entry->{category}
346: ID: @{[sprintf("0x%02x%02x%02x", @id)]}
347:
348: EOT
349:
350: # Put this information on a temporary array
351: push @{$pdb->{_index}}, $entry;
352: }
353: }
354:
355: # XXX - Make this print out debugging information
356: # _load_rsrc_index
357: # Private function. Read the resource index, for a resource database
358: sub _load_rsrc_index
359: {
360: my $pdb = shift;
361: my $fh = shift; # Input file handle
362: my $i;
363:
364: print "Resource index:\n";
365:
366: # Read each resource index entry in turn
367: for ($i = 0; $i < $pdb->{_numrecs}; $i++)
368: {
369: my $buf; # Input buffer
370:
371: print " Resource index entry $i\n";
372:
373: # Read the next resource index entry
374: my $type;
375: my $id;
376: my $offset;
377: my $entry = {}; # Parsed index entry
378:
379: read $fh, $buf, $IndexRsrcLen;
380: if ($hexdump)
381: {
382: &hexdump(" ", $buf);
383: print "\n";
384: }
385:
386: ($type, $id, $offset) = unpack "a4 n N", $buf;
387:
388: $entry->{type} = $type;
389: $entry->{id} = $id;
390: $entry->{offset} = $offset;
391:
392: print <<EOT;
393: Offset: $offset
394: ID: $id
395: Type: $type
396:
397: EOT
398:
399: push @{$pdb->{_index}}, $entry;
400: }
401: }
402:
403: # _load_appinfo_block
404: # Private function. Read the AppInfo block
405: sub _load_appinfo_block
406: {
407: my $pdb = shift;
408: my $fh = shift; # Input file handle
409: my $len; # Length of AppInfo block
410: my $buf; # Input buffer
411:
412: print "AppInfo block:\n";
413:
414: # Sanity check: make sure we're positioned at the beginning of
415: # the AppInfo block
416: if (tell($fh) > $pdb->{_appinfo_offset})
417: {
418: die "Bad AppInfo offset: expected ",
419: sprintf("0x%08x", $pdb->{_appinfo_offset}),
420: ", but I'm at ",
421: tell($fh), "\n";
422: }
423:
424: # Seek to the right place, if necessary
425: if (tell($fh) != $pdb->{_appinfo_offset})
426: {
427: seek PDB, $pdb->{_appinfo_offset}, 0;
428: }
429:
430: # There's nothing that explicitly gives the size of the
431: # AppInfo block. Rather, it has to be inferred from the offset
432: # of the AppInfo block (previously recorded in
433: # $pdb->{_appinfo_offset}) and whatever's next in the file.
434: # That's either the sort block, the first data record, or the
435: # end of the file.
436:
437: if ($pdb->{_sort_offset})
438: {
439: # The next thing in the file is the sort block
440: $len = $pdb->{_sort_offset} - $pdb->{_appinfo_offset};
441: } elsif ((defined $pdb->{_index}) && @{$pdb->{_index}})
442: {
443: # There's no sort block; the next thing in the file is
444: # the first data record
445: $len = $pdb->{_index}[0]{offset} -
446: $pdb->{_appinfo_offset};
447: } else {
448: # There's no sort block and there are no records. The
449: # AppInfo block goes to the end of the file.
450: $len = $pdb->{_size} - $pdb->{_appinfo_offset};
451: }
452:
453: # Read the AppInfo block
454: read $fh, $buf, $len;
455: if ($hexdump)
456: {
457: &hexdump(" ", $buf);
458: print "\n";
459: }
460:
461: # Tell the real class to parse the AppInfo block
462: $pdb->{appinfo} = $pdb->ParseAppInfoBlock($buf);
463:
464: # Print out the parsed values
465: if (ref($pdb->{appinfo}) ne "")
466: {
467: &dumphash($pdb->{appinfo}, "\t");
468: print "\n";
469: }
470: }
471:
472: # _load_sort_block
473: # Private function. Read the sort block.
474: sub _load_sort_block
475: {
476: my $pdb = shift;
477: my $fh = shift; # Input file handle
478: my $len; # Length of sort block
479: my $buf; # Input buffer
480:
481: print "Sort block:\n";
482:
483: # Sanity check: make sure we're positioned at the beginning of
484: # the sort block
485: if (tell($fh) > $pdb->{_sort_offset})
486: {
487: die "Bad sort block offset: expected ",
488: sprintf("0x%08x", $pdb->{_sort_offset}),
489: ", but I'm at ",
490: tell($fh), "\n";
491: }
492:
493: # Seek to the right place, if necessary
494: if (tell($fh) != $pdb->{_sort_offset})
495: {
496: seek PDB, $pdb->{_sort_offset}, 0;
497: }
498:
499: # There's nothing that explicitly gives the size of the sort
500: # block. Rather, it has to be inferred from the offset of the
501: # sort block (previously recorded in $pdb->{_sort_offset})
502: # and whatever's next in the file. That's either the first
503: # data record, or the end of the file.
504:
505: if (defined($pdb->{_index}))
506: {
507: # The next thing in the file is the first data record
508: $len = $pdb->{_index}[0]{offset} -
509: $pdb->{_sort_offset};
510: } else {
511: # There are no records. The sort block goes to the end
512: # of the file.
513: $len = $pdb->{_size} - $pdb->{_sort_offset};
514: }
515:
516: # Read the AppInfo block
517: read $fh, $buf, $len;
518: if ($hexdump)
519: {
520: &hexdump(" ", $buf);
521: print "\n";
522: }
523:
524: # XXX - Check to see if the sort block has some predefined
525: # structure. If so, it might be a good idea to parse the sort
526: # block here.
527:
528: # Tell the real class to parse the sort block
529: $pdb->{sort} = $pdb->ParseSortBlock($buf);
530: }
531:
532: # _load_records
533: # Private function. Load the actual data records, for a record database
534: # (PDB)
535: sub _load_records
536: {
537: my $pdb = shift;
538: my $fh = shift; # Input file handle
539: my $i;
540:
541: print "Records:\n";
542: # Read each record in turn
543: for ($i = 0; $i < $pdb->{_numrecs}; $i++)
544: {
545: my $len; # Length of record
546: my $buf; # Input buffer
547:
548: print " Record $i\n";
549:
550: # Sanity check: make sure we're where we think we
551: # should be.
552: if (tell($fh) > $pdb->{_index}[$i]{offset})
553: {
554: # XXX - The two NULs are really optional.
555: # die "Bad offset for record $i: expected ",
556: # sprintf("0x%08x",
557: # $pdb->{_index}[$i]{offset}),
558: # " but it's at ",
559: # sprintf("[0x%08x]", tell($fh)), "\n";
560: }
561:
562: # Seek to the right place, if necessary
563: if (tell($fh) != $pdb->{_index}[$i]{offset})
564: {
565: seek PDB, $pdb->{_index}[$i]{offset}, 0;
566: }
567:
568: # Compute the length of the record: the last record
569: # extends to the end of the file. The others extend to
570: # the beginning of the next record.
571: if ($i == $pdb->{_numrecs} - 1)
572: {
573: # This is the last record
574: $len = $pdb->{_size} -
575: $pdb->{_index}[$i]{offset};
576: } else {
577: # This is not the last record
578: $len = $pdb->{_index}[$i+1]{offset} -
579: $pdb->{_index}[$i]{offset};
580: }
581:
582: # Read the record
583: read $fh, $buf, $len;
584: if ($hexdump)
585: {
586: &hexdump(" ", $buf);
587: print "\n";
588: }
589:
590: # Tell the real class to parse the record data. Pass
591: # &ParseRecord all of the information from the index,
592: # plus a "data" field with the raw record data.
593: my $record;
594:
595: $record = $pdb->ParseRecord(
596: %{$pdb->{_index}[$i]},
597: "data" => $buf,
598: );
599:
600: my $pass = 12345;
601: if ($i == 0) {
602: unless (keyring_verify($record->{data}, $pass) ) {
603: die "Incorrect password!";
604: }
605: } else {
606: $record->{decrypted} = decrypt($record->{data}, $pass);
607: }
608: push @{$pdb->{records}}, $record;
609:
610: if ($hexdump)
611: {
612: &hexdump(" ", $record->{decrypted});
613: print "\n";
614: }
615:
616: # Print out the parsed values
617: &dumphash($record, "\t");
618: print "\n";
619: }
620: use Data::Dumper;
621: print Dumper $pdb;
622: }
623:
624: # _load_resources
625: # Private function. Load the actual data resources, for a resource database
626: # (PRC)
627: sub _load_resources
628: {
629: my $pdb = shift;
630: my $fh = shift; # Input file handle
631: my $i;
632:
633: print "Resources:\n";
634: # Read each resource in turn
635: for ($i = 0; $i < $pdb->{_numrecs}; $i++)
636: {
637: my $len; # Length of record
638: my $buf; # Input buffer
639:
640: print " Resource $i\n";
641:
642: # Sanity check: make sure we're where we think we
643: # should be.
644: if (tell($fh) > $pdb->{_index}[$i]{offset})
645: {
646: die "Bad offset for resource $i: expected ",
647: sprintf("0x%08x",
648: $pdb->{_index}[$i]{offset}),
649: " but it's at ",
650: sprintf("0x%08x", tell($fh)), "\n";
651: }
652:
653: # Seek to the right place, if necessary
654: if (tell($fh) != $pdb->{_index}[$i]{offset})
655: {
656: seek PDB, $pdb->{_index}[$i]{offset}, 0;
657: }
658:
659: # Compute the length of the resource: the last
660: # resource extends to the end of the file. The others
661: # extend to the beginning of the next resource.
662: if ($i == $pdb->{_numrecs} - 1)
663: {
664: # This is the last resource
665: $len = $pdb->{_size} -
666: $pdb->{_index}[$i]{offset};
667: } else {
668: # This is not the last resource
669: $len = $pdb->{_index}[$i+1]{offset} -
670: $pdb->{_index}[$i]{offset};
671: }
672:
673: # Read the resource
674: read $fh, $buf, $len;
675: if ($hexdump)
676: {
677: &hexdump(" ", $buf);
678: print "\n";
679: }
680:
681: # Tell the real class to parse the resource data. Pass
682: # &ParseResource all of the information from the
683: # index, plus a "data" field with the raw resource
684: # data.
685: my $resource;
686:
687: $resource = $pdb->ParseResource(
688: %{$pdb->{_index}[$i]},
689: "data" => $buf,
690: );
691: push @{$pdb->{resources}}, $resource;
692:
693: # Print out the parsed values
694: &dumphash($resource, "\t");
695: print "\n";
696: }
697: }
698:
699: sub hexdump
700: {
701: my $prefix = shift; # What to print in front of each line
702: my $data = shift; # The data to dump
703: my $maxlines = shift; # Max # of lines to dump
704: my $offset; # Offset of current chunk
705:
706: for ($offset = 0; $offset < length($data); $offset += 16)
707: {
708: my $hex; # Hex values of the data
709: my $ascii; # ASCII values of the data
710: my $chunk; # Current chunk of data
711:
712: last if defined($maxlines) && ($offset >= ($maxlines * 16));
713:
714: $chunk = substr($data, $offset, 16);
715:
716: ($hex = $chunk) =~ s/./sprintf "%02x ", ord($&)/ges;
717:
718: ($ascii = $chunk) =~ y/\040-\176/./c;
719:
720: printf "%s %-48s|%-16s|\n", $prefix, $hex, $ascii;
721: }
722: }
723:
724: # XXX - Ought to have a &dumparray as well. The two can call each other
725: # recursively.
726:
727: sub dumphash
728: {
729: my $hash = shift;
730: my $indent = shift;
731: my $key;
732: my $value;
733:
734: while (($key, $value) = each %{$hash})
735: {
736: if (ref($value) eq "HASH")
737: {
738: print $indent, $key, ":\n";
739: &dumphash($value, $indent . "\t");
740: } elsif (ref($value) eq "ARRAY")
741: {
742: my($i,$j);
743:
744: print $indent, $key, ":\n";
745: for ($i = 0; $i <= $#{$value}; $i++)
746: {
747: if (ref($value->[$i]) eq "HASH")
748: {
749: print $indent, " $i:\n";
750: &dumphash($value->[$i],
751: $indent . "\t");
752: } elsif (ref($value->[$i]) eq "ARRAY")
753: {
754: my @v2 = @{$value->[$i]};
755: for ($j = 0; $j <= $#v2; $j++)
756: {
757: print $indent,
758: "\t$i-$j: [$v2[$j]]\n";
759: }
760: }else {
761: print $indent,
762: "\t$i: [$value->[$i]]\n";
763: }
764: }
765: } else {
766: print $indent, $key, " -> [", $value, "]\n";
767: }
768: }
769: }
770:
771: __END__
772:
773: =head1 NAME
774:
775: pdbdump - Print the contents of a Palm PDB file
776:
777: =head1 SYNOPSIS
778:
779: C<pdbdump> I<[options]> F<filename>
780:
781: =head1 DESCRIPTION
782:
783: C<pdbdump> reads a PalmOS F<.pdb> file, parses it, and prints its
784: contents. This includes both a hex dump of the raw data of each piece,
785: and a human-readable list of the various values, insofar as possible.
786: The aim of C<pdbdump> is to allow one to verify whether a particular
787: file is a well-formed PalmOS database file and if not, where the error
788: lies.
789:
790: If the database is of a known type, C<pdbdump> parses the AppInfo
791: block and records. Otherwise, it simply prints out a hex dump of their
792: contents. C<pdbdump> includes, by default, support for most of the
793: built-in applications. Other helper modules may be loaded with the
794: C<-M> option.
795:
796: =head1 OPTIONS
797:
798: =over 4
799:
800: =item -h -help --help
801:
802: Print a usage message and exit.
803:
804: =item -nohex
805:
806: Don't print the hex dump of the various parts.
807:
808: =item -MI<module>
809:
810: C<use> the named module. This can be useful for loading additional
811: helper modules.
812:
813: =back
814:
815: =head1 BUGS
816:
817: C<pdbdump> only recognizes record databases (C<.pdb> files), not
818: resource databases (C<.prc> files).
819:
820: =head1 SEE ALSO
821:
822: Palm::PDB(3)
823:
824: =head1 AUTHOR
825:
826: Andrew Arensburger <arensb@ooblick.com>
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>